Added Ledger, started removing Holdings

This commit is contained in:
Steve 2025-07-04 12:01:26 +01:00
parent e4f6c3d707
commit 5516aa1362
15 changed files with 2739 additions and 444 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,138 @@
{
"Version": 1,
"WorkspaceRootPath": "\\\\OZHOST1\\d$\\Documents\\Visual Studio Projects\\SharePrices\\",
"Documents": [],
"WorkspaceRootPath": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\scripts\\shareprices.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}",
"RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}"
},
{
"AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\shareprices.aspx||{57312C73-6202-49E9-B1E1-40EA1A6DC1F6}",
"RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\shareprices.aspx||{57312C73-6202-49E9-B1E1-40EA1A6DC1F6}"
},
{
"AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\scripts\\shareprices_ledger.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}",
"RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices_ledger.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}"
},
{
"AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\scripts\\shareprices_holdings.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}",
"RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices_holdings.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}"
},
{
"AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|c:\\users\\tsuser\\sources\\repos\\steves_code\\websites\\shareprices\\shareprices\\sharepricesstyles.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}",
"RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\sharepricesstyles.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}"
},
{
"AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\scripts\\shareprices_accounts.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}",
"RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices_accounts.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}"
},
{
"AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\shareprices.aspx.vb||{2C015C70-C72C-11D0-88C3-00A0C9110049}",
"RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\shareprices.aspx.vb||{2C015C70-C72C-11D0-88C3-00A0C9110049}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": []
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 6,
"Children": [
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "SharePricesStyles.css",
"DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePricesStyles.css",
"RelativeDocumentMoniker": "SharePrices\\SharePricesStyles.css",
"ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePricesStyles.css",
"RelativeToolTip": "SharePrices\\SharePricesStyles.css",
"ViewState": "AgIAAFYAAAAAAAAAAADgv2wAAAAHAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003000|",
"WhenOpened": "2025-05-15T16:16:53.007Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "SharePrices_Accounts.js",
"DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Accounts.js",
"RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices_Accounts.js",
"ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Accounts.js",
"RelativeToolTip": "SharePrices\\scripts\\SharePrices_Accounts.js",
"ViewState": "AgIAAPoAAAAAAAAAAAAAAPoAAAAeAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|",
"WhenOpened": "2025-05-12T12:30:52.565Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "SharePrices_Ledger.js",
"DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Ledger.js",
"RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices_Ledger.js",
"ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Ledger.js*",
"RelativeToolTip": "SharePrices\\scripts\\SharePrices_Ledger.js*",
"ViewState": "AgIAAEsAAACAmZmZmVklwG4AAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|",
"WhenOpened": "2025-05-11T19:04:49.08Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "SharePrices.aspx.vb",
"DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx.vb",
"RelativeDocumentMoniker": "SharePrices\\SharePrices.aspx.vb",
"ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx.vb",
"RelativeToolTip": "SharePrices\\SharePrices.aspx.vb",
"ViewState": "AgIAALUAAAAAAAAAAAAQwH4BAAA2AAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003361|",
"WhenOpened": "2025-02-10T17:47:33.143Z"
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "SharePrices_Holdings.js",
"DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Holdings.js",
"RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices_Holdings.js",
"ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Holdings.js",
"RelativeToolTip": "SharePrices\\scripts\\SharePrices_Holdings.js",
"ViewState": "AgIAAFIEAAAA8P////8BwMADAAAsAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|",
"WhenOpened": "2025-02-10T17:39:46.261Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "SharePrices.aspx",
"DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx",
"RelativeDocumentMoniker": "SharePrices\\SharePrices.aspx",
"ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx",
"RelativeToolTip": "SharePrices\\SharePrices.aspx",
"LogicalView": "7651a703-06e5-11d1-8ebd-00a0c90f26ea",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000195|",
"WhenOpened": "2025-01-06T10:39:09.87Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "SharePrices.js",
"DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices.js",
"RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices.js",
"ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices.js",
"RelativeToolTip": "SharePrices\\scripts\\SharePrices.js",
"ViewState": "AgIAADwAAADAZmZmZqYowEwBAAANAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|",
"WhenOpened": "2025-01-06T10:28:28.952Z",
"EditorCaption": ""
}
]
}
]
}
]
}

View File

@ -1,12 +1,135 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\Steve.OZDOMAIN\\source\\repos\\Steves_Code\\Websites\\SharePrices\\",
"Documents": [],
"WorkspaceRootPath": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\scripts\\shareprices_holdings.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}",
"RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices_holdings.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}"
},
{
"AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\scripts\\shareprices_ledger.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}",
"RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices_ledger.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}"
},
{
"AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|c:\\users\\tsuser\\sources\\repos\\steves_code\\websites\\shareprices\\shareprices\\scripts\\shareprices.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}",
"RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}"
},
{
"AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\shareprices.aspx||{57312C73-6202-49E9-B1E1-40EA1A6DC1F6}",
"RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\shareprices.aspx||{57312C73-6202-49E9-B1E1-40EA1A6DC1F6}"
},
{
"AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\sharepricesstyles.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}",
"RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\sharepricesstyles.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}"
},
{
"AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\scripts\\shareprices_accounts.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}",
"RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\scripts\\shareprices_accounts.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}"
},
{
"AbsoluteMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\shareprices\\shareprices.aspx.vb||{2C015C70-C72C-11D0-88C3-00A0C9110049}",
"RelativeMoniker": "D:0:0:{AB459D42-2246-47E4-8F1B-F8325E8B780A}|SharePrices\\SharePrices.vbproj|solutionrelative:shareprices\\shareprices.aspx.vb||{2C015C70-C72C-11D0-88C3-00A0C9110049}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": []
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 4,
"Children": [
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "SharePricesStyles.css",
"DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePricesStyles.css",
"RelativeDocumentMoniker": "SharePrices\\SharePricesStyles.css",
"ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePricesStyles.css",
"RelativeToolTip": "SharePrices\\SharePricesStyles.css",
"ViewState": "AgIAAFYAAAAAAAAAAADgv2wAAAAHAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003000|",
"WhenOpened": "2025-05-15T16:16:53.007Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "SharePrices_Accounts.js",
"DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Accounts.js",
"RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices_Accounts.js",
"ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Accounts.js",
"RelativeToolTip": "SharePrices\\scripts\\SharePrices_Accounts.js",
"ViewState": "AgIAAPoAAAAAAAAAAAAAAPoAAAAeAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|",
"WhenOpened": "2025-05-12T12:30:52.565Z"
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "SharePrices_Ledger.js",
"DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Ledger.js",
"RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices_Ledger.js",
"ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Ledger.js",
"RelativeToolTip": "SharePrices\\scripts\\SharePrices_Ledger.js",
"ViewState": "AgIAAEsAAACAmZmZmVklwG4AAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|",
"WhenOpened": "2025-05-11T19:04:49.08Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "SharePrices.aspx.vb",
"DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx.vb",
"RelativeDocumentMoniker": "SharePrices\\SharePrices.aspx.vb",
"ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx.vb",
"RelativeToolTip": "SharePrices\\SharePrices.aspx.vb",
"ViewState": "AgIAALUAAAAAAAAAAAAQwH4BAAA2AAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003361|",
"WhenOpened": "2025-02-10T17:47:33.143Z"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "SharePrices_Holdings.js",
"DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Holdings.js",
"RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices_Holdings.js",
"ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices_Holdings.js",
"RelativeToolTip": "SharePrices\\scripts\\SharePrices_Holdings.js",
"ViewState": "AgIAAJcEAAAAyAAAAEAgwLgEAAARAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|",
"WhenOpened": "2025-02-10T17:39:46.261Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "SharePrices.aspx",
"DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx",
"RelativeDocumentMoniker": "SharePrices\\SharePrices.aspx",
"ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\SharePrices.aspx",
"RelativeToolTip": "SharePrices\\SharePrices.aspx",
"LogicalView": "7651a703-06e5-11d1-8ebd-00a0c90f26ea",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000195|",
"WhenOpened": "2025-01-06T10:39:09.87Z"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "SharePrices.js",
"DocumentMoniker": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices.js",
"RelativeDocumentMoniker": "SharePrices\\scripts\\SharePrices.js",
"ToolTip": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\SharePrices\\scripts\\SharePrices.js",
"RelativeToolTip": "SharePrices\\scripts\\SharePrices.js",
"ViewState": "AgIAADwAAADAZmZmZqYowEwBAAANAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|",
"WhenOpened": "2025-01-06T10:28:28.952Z",
"EditorCaption": ""
}
]
}
]
}
]
}

View File

@ -12,20 +12,36 @@ Public Class DataAccessLayer
Public Volume As Int64
End Class
Public Shared Function GetInstruments() As String
Dim c As SqlCommand = GetSQLCommand("usp_GetInstruments")
Dim Instruments As String = DataReaderToJSONString(c.ExecuteReader())
c = GetSQLCommand("usp_GetHoldings")
Dim Holdings As String = DataReaderToJSONString(c.ExecuteReader())
c = GetSQLCommand("usp_GetHoldingsHistory")
Dim HoldingsHistory As String = DataReaderToJSONString(c.ExecuteReader())
c = GetSQLCommand("usp_GetAccounts")
Dim Accounts As String = DataReaderToJSONString(c.ExecuteReader())
GetInstruments = "{""Instruments"": " + Instruments + ", ""Holdings"": " + Holdings + ", ""HoldingsHistory"": " + HoldingsHistory + ", ""Accounts"": " + Accounts + "}"
DisposeSQLCommand(c)
End Function
Public Shared Function GetInstruments() As String
Dim c As SqlCommand = GetSQLCommand("usp_GetInstruments")
Dim Instruments As String = DataReaderToJSONString(c.ExecuteReader())
c = GetSQLCommand("usp_GetHoldings")
Dim Holdings As String = DataReaderToJSONString(c.ExecuteReader())
c = GetSQLCommand("usp_GetHoldingsHistory")
Dim HoldingsHistory As String = DataReaderToJSONString(c.ExecuteReader())
c = GetSQLCommand("usp_GetAccounts")
Dim Accounts As String = DataReaderToJSONString(c.ExecuteReader())
GetInstruments = "{""Instruments"": " + Instruments + ", ""Holdings"": " + Holdings + ", ""HoldingsHistory"": " + HoldingsHistory + ", ""Accounts"": " + Accounts + "}"
DisposeSQLCommand(c)
End Function
Public Shared Function AddInstrument(Symbol As String, FullName As String) As String
Public Shared Function GetAggregatedCurrentHoldings() As String
Dim c As SqlCommand = GetSQLCommand("usp_GetAggregatedCurrentHoldings")
GetAggregatedCurrentHoldings = DataReaderToJSONString(c.ExecuteReader())
DisposeSQLCommand(c)
End Function
Public Shared Function GetCurrentHoldings() As String
Dim c As SqlCommand = GetSQLCommand("usp_GetCurrentHoldings")
GetCurrentHoldings = DataReaderToJSONString(c.ExecuteReader())
DisposeSQLCommand(c)
End Function
Public Shared Function GetTradeHistory() As String
Dim c As SqlCommand = GetSQLCommand("usp_GetTradeHistory")
GetTradeHistory = DataReaderToJSONString(c.ExecuteReader())
DisposeSQLCommand(c)
End Function
Public Shared Function AddInstrument(Symbol As String, FullName As String) As String
Dim c As SqlCommand = GetSQLCommand("usp_InsertInstrument")
c.Parameters.AddWithValue("Symbol", Symbol)
c.Parameters.AddWithValue("FullName", FullName)
@ -258,6 +274,7 @@ Public Class DataAccessLayer
Select Case dr.GetDataTypeName(fieldNo).ToUpper
Case "CHAR", "VARCHAR" : thisRow += """" + dr(fieldNo) + """"
Case "DATETIME", "SMALLDATETIME", "DATETIME2", "DATE" : thisRow += ToEpochTime(dr(fieldNo)).ToString()
Case "BIT" : If (dr(fieldNo)) Then thisRow += "true" Else thisRow += "false"
Case Else : thisRow += dr(fieldNo).ToString()
End Select
End If

View File

@ -40,7 +40,8 @@
<a id="navCharts" class="activenav" onclick="showTab(this)" data-div="chartDiv">Charts</a>
<a id="navCurrentHoldings" onclick="showTab(this);" data-div="holdingsDiv">Holdings</a>
<a id="navAccounts" onclick="showTab(this);" data-div="accountsDiv">Accounts</a>
<a id="navAnal" onclick="showTab(this);" data-div="analDiv">Analysis</a>
<a id="navLedger" onclick="showTab(this); importedFunctions.updateLedgerTable();" data-div="ledgerDiv">Ledger</a>
<!--<a id="navAnal" onclick="showTab(this);" data-div="analDiv">Analysis</a>!-->
<a id="navHist" onclick="showTab(this); importedFunctions.refreshHistoryChart();" data-div="histDiv">History</a>
<a id="navLog" onclick="showTab(this);" data-div="logDiv">Log</a>
</td>
@ -73,12 +74,26 @@
</div>
<div class="tabParent">
<div class="tabContainer" id="holdingsDiv">
<!-- Old holdings table
<div>
<label for="groupBySymbol">Group By Symbol</label><input type="checkbox" name="groupBySymbol" id="groupBySymbol" onclick="handleClick(this)"/>
<label for="groupBySymbol">Group By Symbol</label>
<input type="checkbox" name="groupBySymbol" id="groupBySymbol" onclick="handleClick(this)"/>
</div>
<div id="holdingsTableDiv">Holdings table</div>
<br />
!-->
<div id="aggHoldingsTableDiv">Aggregated Holdings table</div>
<br />
<div>
<label for="showSubtotals">Show Tax Bracket Subtotals</label>
<input type="checkbox" name="showSubtotals" id="showSubtotals" onclick="handleClick(this)"/>
<label for="showCryptoHoldings">Show Cryptocurrency Holdings</label>
<input type="checkbox" name="showCryptoHoldings" id="showCryptoHoldings" onclick="handleClick(this)"/>
</div>
<div id="newHoldingsTableDiv">New Holdings table</div>
<br />
<div id="watchlistTableDiv">Watchlist table</div>
<!-- Old pie charts
<br />
<table>
<tr>
@ -91,6 +106,7 @@
<td>Current Value per Account<div id="divAccountValue" class="HoldingCurrenciesChart"></div></td>
</tr>
</table>
!-->
</div>
</div>
<div class="tabParent">
@ -101,9 +117,14 @@
<div id="accountsSummaryDiv"></div>
</div>
</div>
<div class="tabParent">
<div class="tabContainer" id="ledgerDiv">Analysis</div>
</div>
<!--
<div class="tabParent">
<div class="tabContainer" id="analDiv">Analysis</div>
</div>
!-->
<div class="tabParent">
<div class="tabContainer" id="histDiv" style="height: 85%;">History Chart</div>
<!--<span onclick="refreshHistoryChart();">Refresh Chart</span>!-->
@ -141,6 +162,7 @@
<div id="chartTooltip" class="chart-tooltip"></div>
<script>
let importedFunctions = {};
function logInfo(msg) {
console.info(msg);
if (typeof msg == 'object') {
@ -202,6 +224,10 @@
break;
case 'hideEmptyAccounts':
break;
case 'showSubtotals':
break;
case 'showCryptoHoldings':
break;
}
}
</script>
@ -209,7 +235,6 @@
import {
addInstrument,
createSharesTable,
createSharesTableRow,
getInstruments,
redrawAllSharesCharts,
refreshHistoryChart,
@ -220,10 +245,12 @@
showModalDialog_FullScreenChart,
showModalDialog_SoldHolding
} from "./scripts/SharePrices.js";
import { updateLedgerTable } from "./scripts/SharePrices_Ledger.js";
importedFunctions = {
addInstrument: addInstrument,
createSharesTable: createSharesTable,
createSharesTableRow: createSharesTableRow,
getInstruments: getInstruments,
redrawAllSharesCharts: redrawAllSharesCharts,
refreshHistoryChart: refreshHistoryChart,
@ -232,7 +259,8 @@
showModalDialog_AddInstrument: showModalDialog_AddInstrument,
showModalDialog_AddHolding: showModalDialog_AddHolding,
showModalDialog_FullScreenChart: showModalDialog_FullScreenChart,
showModalDialog_SoldHolding: showModalDialog_SoldHolding
showModalDialog_SoldHolding: showModalDialog_SoldHolding,
updateLedgerTable: updateLedgerTable
}
function initPage() {
@ -258,7 +286,13 @@
$("#groupBySymbol").prop('checked', (c == 'true') ? true : false);
c = Cookies.get('hideEmptyAccounts');
$("#hideEmptyAccounts").prop('checked', (c == 'true') ? true : false);
$("#hideEmptyAccounts").prop('checked', (c == 'true') ? true : false)
c = Cookies.get('showSubtotals');
$("#showSubtotals").prop('checked', (c == 'true') ? true : false);
c = Cookies.get('showCryptoHoldings');
$("#showCryptoHoldings").prop('checked', (c == 'true') ? true : false);
getInstruments();
}

View File

@ -105,19 +105,29 @@ Public Class SharePrices
'Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.SubmitDailyData(" + Symbol + ") completed. Duration = " + DateDiff(DateInterval.Second, startDT, endDT).ToString())
End Sub
<WebMethod()>
Public Shared Sub SubmitIntradayData(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData), GMTOffset As Integer, Currency As String, CurrentPrice As Double, InstrumentType As String, TradeDayStart As Long, TradeDayEnd As Long)
'Public Shared Sub SubmitIntradayData(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData))
Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.SubmitIntradayData webmethod: " + Symbol)
<WebMethod()>
Public Shared Sub SubmitIntradayData(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData), GMTOffset As Integer, Currency As String, CurrentPrice As Double, InstrumentType As String, TradeDayStart As Long, TradeDayEnd As Long)
'Public Shared Sub SubmitIntradayData(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData))
Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.SubmitIntradayData webmethod: " + Symbol)
DataAccessLayer.UpdateInstrument(Symbol, GMTOffset, Currency, CurrentPrice, InstrumentType, FromEpochTime(TradeDayStart), FromEpochTime(TradeDayEnd))
DataAccessLayer.UpdateInstrument(Symbol, GMTOffset, Currency, CurrentPrice, InstrumentType, FromEpochTime(TradeDayStart), FromEpochTime(TradeDayEnd))
Dim responseText As String = DataAccessLayer.InsertIntradayData(Symbol, DailyPrices)
If responseText = "" Then responseText = "[]"
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
End Sub
Dim responseText As String = DataAccessLayer.InsertIntradayData(Symbol, DailyPrices)
If responseText = "" Then responseText = "[]"
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
End Sub
<WebMethod()>
<WebMethod()>
Public Shared Sub UpdateInstrument(Symbol As String, GMTOffset As Integer, Currency As String, CurrentPrice As Double, InstrumentType As String, TradeDayStart As Long, TradeDayEnd As Long)
Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.UpdateInstrument webmethod: " + Symbol)
DataAccessLayer.UpdateInstrument(Symbol, GMTOffset, Currency, CurrentPrice, InstrumentType, FromEpochTime(TradeDayStart), FromEpochTime(TradeDayEnd))
Dim responseText As String = "{ ""Result"": ""Success"" }"
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
End Sub
<WebMethod()>
Public Shared Sub SwapInstrumentDisplayOrders(Symbol1 As String, Symbol2 As String)
Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.SwapInstrumentDisplayOrders webmethod: " + Symbol1 + ", " + Symbol2)
@ -152,7 +162,8 @@ Public Class SharePrices
'webClient.Proxy = New System.Net.WebProxy("127.0.0.1", 8888)
'Set headers required by Yahoo Finance
webClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0")
'webClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0")
webClient.Headers.Add("User-Agent", "MMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36")
webClient.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
webClient.Headers.Add("Accept-Language", "en-GB,en;q=0.5")
'webClient.Headers.Add("Accept-Encoding", "gzip, deflate, br")
@ -163,6 +174,10 @@ Public Class SharePrices
webClient.Headers.Add("Sec-Fetch-Site", "none()")
webClient.Headers.Add("Sec-Fetch-User", "?1")
webClient.Headers.Add("sec-ch-ua", "Google Chrome"";v=""135"", ""Not-A.Brand"";v=""8"", ""Chromium"";v=""135""")
webClient.Headers.Add("sec-ch-ua-mobile", "70")
webClient.Headers.Add("sec-ch-ua-platform", "Windows")
DownloadYahooFinanceWebString = webClient.DownloadString(queryString)
End Function
<WebMethod()>
@ -355,21 +370,22 @@ Public Class SharePrices
Public Shared Sub SearchYahooFinanceShares(SearchString As String)
Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.SearchYahooFinanceShares webmethod: " + SearchString)
Dim webClient As New System.Net.WebClient
'Dim webClient As New System.Net.WebClient
webClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0")
webClient.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
webClient.Headers.Add("Accept-Language", "en-GB,en;q=0.5")
'webClient.Headers.Add("Accept-Encoding", "gzip, deflate, br")
'Connection: keep -alive
webClient.Headers.Add("Upgrade-Insecure-Requests", "1")
webClient.Headers.Add("Sec-Fetch-Dest", "document()")
webClient.Headers.Add("Sec-Fetch-Mode", "navigate()")
webClient.Headers.Add("Sec-Fetch-Site", "none()")
webClient.Headers.Add("Sec-Fetch-User", "?1")
'webClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0")
'webClient.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
'webClient.Headers.Add("Accept-Language", "en-GB,en;q=0.5")
''webClient.Headers.Add("Accept-Encoding", "gzip, deflate, br")
''Connection: keep -alive
'webClient.Headers.Add("Upgrade-Insecure-Requests", "1")
'webClient.Headers.Add("Sec-Fetch-Dest", "document()")
'webClient.Headers.Add("Sec-Fetch-Mode", "navigate()")
'webClient.Headers.Add("Sec-Fetch-Site", "none()")
'webClient.Headers.Add("Sec-Fetch-User", "?1")
Dim responseText As String = webClient.DownloadString("https://query1.finance.yahoo.com/v1/finance/search?q=" + SearchString + "&quotesCount=6&newsCount=0&enableFuzzyQuery=false&quotesQueryId=tss_match_phrase_query&multiQuoteQueryId=multi_quote_single_token_query&newsQueryId=news_ss_symbols&enableCb=false&enableNavLinks=false&vespaNewsTimeoutMs=600")
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
'Dim responseText As String = webClient.DownloadString("https://query1.finance.yahoo.com/v1/finance/search?q=" + SearchString + "&quotesCount=6&newsCount=0&enableFuzzyQuery=false&quotesQueryId=tss_match_phrase_query&multiQuoteQueryId=multi_quote_single_token_query&newsQueryId=news_ss_symbols&enableCb=false&enableNavLinks=false&vespaNewsTimeoutMs=600")
Dim responseText As String = DownloadYahooFinanceWebString("https://query1.finance.yahoo.com/v1/finance/search?q=" + SearchString + "&quotesCount=6&newsCount=0&enableFuzzyQuery=false&quotesQueryId=tss_match_phrase_query&multiQuoteQueryId=multi_quote_single_token_query&newsQueryId=news_ss_symbols&enableCb=false&enableNavLinks=false&vespaNewsTimeoutMs=600")
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
End Sub
<WebMethod()>
@ -390,11 +406,36 @@ Public Class SharePrices
<WebMethod()>
Public Shared Sub GetAggregatedCurrentHoldings()
'Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.GetAggregatedCurrentHoldings webmethod")
Dim responseText As String = DataAccessLayer.GetAggregatedCurrentHoldings()
If responseText = "" Then responseText = "[]"
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
End Sub
<WebMethod()>
Public Shared Sub GetCurrentHoldings()
'Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.GetCurrentHoldings webmethod")
Dim responseText As String = DataAccessLayer.GetCurrentHoldings()
If responseText = "" Then responseText = "[]"
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
End Sub
<WebMethod()>
Public Shared Sub GetTradeHistory()
'Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.GetTradeHistory webmethod")
Dim responseText As String = DataAccessLayer.GetTradeHistory()
If responseText = "" Then responseText = "[]"
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
End Sub
<WebMethod()>
<WebMethod()>
Public Shared Sub TestWebMethod(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData))
Dim startDT As Date = Now()
Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.TestWebMethod: " + Symbol)

View File

@ -260,8 +260,10 @@
<Content Include="scripts\jquery\jquery.marquee.min.js" />
<Content Include="scripts\js.cookie-2.2.1.min.js" />
<Content Include="scripts\SharePrices.js" />
<Content Include="scripts\SharePrices_Accounts.js" />
<Content Include="scripts\SharePrices_Charts.js" />
<Content Include="scripts\SharePrices_Holdings.js" />
<Content Include="scripts\SharePrices_Ledger.js" />
<Content Include="scripts\tablesorter\dragtable.mod.css" />
<Content Include="scripts\tablesorter\filter.formatter.css" />
<Content Include="scripts\tablesorter\highlights.css" />

View File

@ -39,6 +39,11 @@ td {vertical-align: top;}
#spnCurrencies a { color: white; }
#spnCurrencies a:visited { color: white; }
.open a:link { color: white; }
.open a:visited { color: white; }
.closed a:link { color: #757575; }
.closed a:visited { color: #757575; }
a:link { color: #f473ff; }
a:visited { color: #9a34b7; }
@ -47,20 +52,49 @@ a:visited { color: #9a34b7; }
.miniHoldings th { font-size: x-small; border: 1px solid #505050; }
.soldHolding { text-decoration: line-through; }
/*tr.highlighted, td.highlighted { background-color: gold; }*/
.highlighted { background-color: gold; }
.mainHoldings { font-size: small; }
table.mainHoldings { width: 100%; }
table.mainHoldings th { background-color: #101040; }
table.mainHoldings th, table.mainHoldings td { padding-left: 5px; padding-right: 5px; }
table.mainHoldings tr:hover { background-color: #404090; }
.mainHoldingsSpacer { font-size: 2pt; }
.holdingsSubtotal td { font-weight: bold; padding-top: 6px; padding-bottom: 12px; }
/*.holdingsSubtotal td { font-weight: bold; }*/
.holdingsSubtotal { font-weight: bold; }
/*.holdingsGrandTotal td { font-weight: bold; padding-top: 10px; padding-bottom: 12px; }*/
/*.holdingsGrandTotal td { font-weight: bold; }*/
.holdingsGrandTotal { font-weight: bold; }
.holdingsRow { background-color: #10101a }
.watchlist { font-size: small; }
/*tr.highlighted, td.highlighted { background-color: gold; }*/
.highlighted { background-color: gold; }
.highlightedProfit { color:black; background-color: #07b200; }
.highlightedLoss { color:white; background-color: #ff4141; }
.tablesorter .filtered { display: none; }
.ledger { font-size: x-small; }
table.ledger { width: 100%; }
table.ledger th { background-color: #101040; }
table.ledger th, table.ledger td { padding-left: 5px; padding-right: 5px; }
table.ledger tr:hover { background-color: #404090; }
.ledgerRow.odd { background-color: #28293b; }
/*.ledgerRow.even { background-color: #090b25 }*/
/*
.newHoldingsTable { font-size: small; }
table.newHoldingsTable { width: 100%; }
table.newHoldingsTable th, table.newHoldingsTable td { padding-left: 5px; padding-right: 5px; }
table.newHoldingsTable tr:hover { background-color: #404090; }
*/
.watchlist {
font-size: small;
}
/*table.watchlist { width: 100%; }*/
table.watchlist th, table.watchlist td { padding-left: 5px; padding-right: 5px; }
table.watchlist tr:hover { background-color: #404090; }
.accounts { font-size: small; }
.accounts { font-size: 9pt; }
/*table.accounts { width: 100%; }*/
table.accounts th, table.accounts td { padding-left: 5px; padding-right: 5px; }
table.accounts tr:hover { background-color: #404090; }
@ -137,8 +171,6 @@ td.no-border { border: 0px; text-align: right; }
border-radius: 4px;
}
.deletebutton { color: red; cursor: pointer; }
span.addHolding { cursor: pointer; }
@ -147,142 +179,3 @@ span.addHolding { cursor: pointer; }
.marquee { width: 600px; overflow: hidden; font-size: small; /*border: 1px solid #ccc;*/ /*background: #ccc;*/ }
.marquee div { padding: 1px 0px 1px 0px; }
/*
.highcharts-hollowcandlestick-series .highcharts-point-down {
fill: #ff4141;
stroke: #ff4141;
}*/
/*.highcharts-hollowcandlestick-series .highcharts-point-down-bearish-up {
fill: #35bd00;
stroke: #35bd00;
}*/
/*
.highcharts-hollowcandlestick-series .highcharts-point-up {
fill: #07b200;
stroke: #07b200;
}*/
/*
html, body { min-height: 100% !important; height: 100%; width: 100%; }
body { font-family: Verdana, Arial, Helvetica, sans-serif; margin-left: 0px; background-color: black; color: #B0B0B0; }
/ * The navigation bar * /
.navbar { overflow: hidden; background-color: #333; position: fixed; top: 0; width: 100%; z-index: 100; }
.navbar a { float: left; display: block; color: #f2f2f2; text-align: center; padding: 7px 16px; text-decoration: none; }
.navbar label { color: #f2f2f2; }
.navbar a:hover { background: #ddd; color: white; }
.activenav { float: left; display: block; color: #f2f2f2 !important; background-color: #307D30 !important; text-align: center; padding: 7px 16px; text-decoration: none; }
.main { height: 100%; width: 100%; margin-top: 34px; / * Add a top margin to avoid content overlay * / }
/ * #chartDiv { height: 100%; overflow: scroll; } * /
#chartDiv { margin-top: 34px; }
#holdingsDiv { margin-top: 34px; }
#accoountsDiv { margin-top: 34px; }
#analDiv { margin-top: 34px; }
div.activetab {position: absolute; top: 0; height: 100%; width: 100%; z-index: 2; background-color: black; overflow: scroll; / *display: inline;* /}
div.inactivetab {position: absolute; top: 0; height:100%; width: 100%; z-index: 0; overflow: scroll; / *display: none;* /}
td {vertical-align: top;}
.shareRow { / *background-color: #ffffff;* / }
.altShareRow { background-color: #212121; / *#2a2a2a;* / }
.instrumentName { color: white; }
#spnTotalHoldings { color: white; padding-left: 50px; }
a:link { color: #f473ff; }
a:visited { color: #9a34b7; }
.miniHoldings { border-collapse: collapse; border-spacing: 0; }
.miniHoldings td { font-size: x-small; border: 1px solid #505050; }
.miniHoldings th { font-size: x-small; border: 1px solid #505050; }
.soldHolding { text-decoration: line-through; }
tr.highlighted, td.highlighted { background-color: gold; }
.mainHoldings { font-size: small; }
table.mainHoldings { width: 100%; }
table.mainHoldings th, table.mainHoldings td { padding-left: 5px; padding-right: 5px; }
table.mainHoldings tr:hover { background-color: #404090; }
.accounts { font-size: small; }
/ *table.accounts { width: 100%; }* /
table.accounts th, table.accounts td { padding-left: 5px; padding-right: 5px; }
table.accounts tr:hover { background-color: #404090; }
.analysis { font-size: small; }
table.analysis { width: 100%; }
table.analysis th, table.analysis td { padding-left: 5px; padding-right: 5px; }
table.analysis tbody tr { white-space: nowrap; }
table.analysis tr:hover { background-color: #404090; }
.pcLabel { position: relative; background-color: transparent; text-align: center; z-index: 2; }
.pcBackground { position: absolute; z-index: 1; }
.price-summary { font-size: x-small; }
.current-value { font-size: medium; }
.num {text-align: right;}
.summary { width: 100%; border-collapse: collapse; }
.spacer { padding-top: 6px; }
.profit { color: #07b200; / * limegreen * /}
.loss { color: #ee2727; / *red;* / / * firebrick * /}
td.no-border { border: 0px; text-align: right; }
.flot-tick-label { color: #b0b0b0 }
.LongSummary { font-size: x-small; }
.LongChart { height: 150px; width: 300px; }
.MidSummary { font-size: x-small; }
.MidChart { height: 150px; width: 300px; }
.ShortSummary { font-size: x-small; }
.ShortChart { height: 150px; width: 300px; }
.DaySummary { font-size: x-small; }
.DayChart { height: 136px; width: 300px; }
.FullScreenChart { height: 300px; width: 400px; }
.HoldingCurrenciesChart { height: 250px; width: 250px; }
/ *#divHoldingCurrenciesChart .pieLabel { color: red !important; background-color: black; }* /
#divHoldingCurrenciesChart div.pieLabel { font-size: x-small; text-align: center; padding: 2px; color: white; background-color: black }
#divHoldingCurrenciesChartCurrentValue div.pieLabel { font-size: x-small; text-align: center; padding: 2px; color: white; background-color: black }
#divAccountBookCost div.pieLabel { font-size: x-small; text-align: center; padding: 2px; color: white; background-color: black }
#divAccountValue div.pieLabel { font-size: x-small; text-align: center; padding: 2px; color: white; background-color: black }
.flotTip
{
padding: 3px 5px;
background-color: #000;
z-index: 101;
color: #fff;
box-shadow: 0 0 10px #555;
opacity: .7;
filter: alpha(opacity=70);
border: 2px solid #fff;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.deletebutton { color: red; cursor: pointer; }
span.addHolding { cursor: pointer; }
.flot-text { font-size: x-small !important; }
.chart-tooltip { position: absolute; border: 1px solid #fdd; padding: 2px; background-color: #fee; opacity: 0.80; display: none; z-index: 6; }
#selectInstrument2 { width: 100%; }
*/

View File

@ -1,12 +1,12 @@
// @ts-check
//Testing gitea.copeland-bowen.com #3
import {formatAmount, generateAxisBreaks, getProfitOrLoss, timespans} from "./SharePrices_Common.js";
import {createLongChart, createMidChart, createShortChart, createSingleDayChart, createChart_Flot,
createChart, createFullScreenChart, createFullScreenChart_Flot, createFullScreenComparisonChart,
createFullScreenComparisonChart_Flot
} from "./SharePrices_Charts.js";
import {createHoldingsTable} from "./SharePrices_Holdings.js";
import { updateHoldingsTables } from "./SharePrices_Holdings.js";
import { createAccountsTables } from "./SharePrices_Accounts.js";
'use strict';
@ -26,14 +26,43 @@ var vars = {
previousDay: 0,
previousClose: 0,
previousWeek: 0,
previousWeekClose:0
previousWeekClose: 0,
currentHoldings: {},
currentAggHoldingsRows: {
Data: [],
rowID: function (r) {
return (r.Symbol).replaceAll(' ', '_').replaceAll('=', '_');
},
indexOfRowID: function (rowID) {
return this.Data.map(function (item) {
return vars.currentAggHoldingsRows.rowID(item.attributes)
}).indexOf(rowID)
},
GetRow: function (r) {
return this.Data[this.indexOfRowID(this.rowID(r))]
}
},
currentHoldingsRows: {
Data: [],
rowID: function (r) {
return (r.TaxBucket + '_' + r.AccountName + '_' + r.Symbol).replaceAll(' ', '_').replaceAll('=', '_');
},
indexOfRowID: function (rowID) {
return this.Data.map(function (item) {
return vars.currentHoldingsRows.rowID(item.attributes)
}).indexOf(rowID)
},
GetRow: function (r) {
return this.Data[this.indexOfRowID(this.rowID(r))]
}
}
}
//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 currencyFormatter = new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' });
var instrumentSearchResults = [];
var tablesUpdateTimings = { lastUpdate: 0, timeBetweenUpdates: 10000, timer: 0, updateNeeded: true };
var tablesUpdateTimings = { lastUpdate: 0, timeBetweenUpdates: 15000, timer: 0, updateNeeded: true };
var indexMarquee = {
newContent: '',
currentContent: '',
@ -127,7 +156,7 @@ var Instruments = {
let lastFetchedDate = this.Data[0].lastFetchedIntraday;
let lastFetchedIndex = 0;
for (let i = 1; i < this.Data.length; i++) {
if (this.Data[i].Symbol != 'GBP' && this.Data[i].Symbol != 'GBp') {
if (this.Data[i].Symbol != 'GBP' && this.Data[i].Symbol != 'GBp' && this.Data[i].TrackPriceHistory == 1) {
//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 (lastFetchedDate > this.Data[i].lastFetchedIntraday) {
@ -247,6 +276,14 @@ Date.prototype.yyyymmddhhmmss = function () {
var ss = this.getSeconds().toString();
return yyyy + '-' + (mm[1] ? mm : "0" + mm[0]) + '-' + (dd[1] ? dd : "0" + dd[0]) + " " + (hh[1] ? hh : "0" + hh[0]) + ":" + (nn[1] ? nn : "0" + nn[0]) + ":" + (ss[1] ? ss : "0" + ss[0]);
};
Date.prototype.yyyymmddhhmm = function () {
var yyyy = this.getFullYear().toString();
var mm = (this.getMonth() + 1).toString(); // getMonth() is zero-based
var dd = this.getDate().toString();
var hh = this.getHours().toString();
var nn = this.getMinutes().toString();
return yyyy + '-' + (mm[1] ? mm : "0" + mm[0]) + '-' + (dd[1] ? dd : "0" + dd[0]) + " " + (hh[1] ? hh : "0" + hh[0]) + ":" + (nn[1] ? nn : "0" + nn[0]);
};
Date.prototype.yyyymmdd = function () {
var yyyy = this.getFullYear().toString();
var mm = (this.getMonth() + 1).toString(); // getMonth() is zero-based
@ -289,7 +326,11 @@ Number.prototype.autoScale = function () {
return this.toFixed(1);
} else {
if (a < 0.001) {
return this.toExponential(2);
if (a == 0) {
return '0';
} else {
return this.toExponential(2);
}
} else {
return this.toFixed(2);
}
@ -383,7 +424,7 @@ export function createSharesTable() {
logError({ MSG: "Error in createSharesTable", err: err });
}
};
export function createSharesTableRow(instrument) {
function createSharesTableRow(instrument) {
function getHoldingsTable(Instrument) {
if (Instrument.Holdings.length > 0) {
let totalUnits = 0;
@ -769,7 +810,7 @@ function getYahooDailyData(Instrument) {
createLongChart(Instrument);
} else {
//console.info("No data received for symbol: " + Instrument.Symbol);
logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response })
logWarning({ MSG: "getYahooDailyData - No data received for symbol: " + Instrument.Symbol, response: response })
}
},
failure: function (response) {
@ -829,14 +870,14 @@ function getYahooIntradayData(Instrument) {
createMidChart(Instrument);
createShortChart(Instrument);
} else {
console.info("No NEW data received for symbol: " + Instrument.Symbol);
console.info("getYahooIntradayData - No NEW data received for symbol: " + Instrument.Symbol);
}
} else {
//logWarning({ MSG: "No timestamp series received for symbol: " + Instrument.Symbol, response: response });
}
} else {
//console.warn("No data received for symbol: " + Instrument.Symbol);
logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response });
logWarning({ MSG: "getYahooIntradayData - No data received for symbol: " + Instrument.Symbol, response: response });
}
},
failure: function (response) {
@ -859,10 +900,15 @@ function getYahooSingleDayData(Instrument) {
success: function (response) {
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
let newData = [];
let dbUpdateNeeded = false;
if (response.chart && response.chart.result) {
let dataSeries = response.chart.result[0];
if (dataSeries.meta) {
if (Instrument.CurrentPrice != dataSeries.meta.regularMarketPrice) {
dbUpdateNeeded = true;
}
Instrument.SingleDayPreviousClose = dataSeries.meta.previousClose;
Instrument.SingleDayStartDate = dataSeries.meta.currentTradingPeriod.regular.start * 1000;
Instrument.SingleDayEndDate = dataSeries.meta.currentTradingPeriod.regular.end * 1000;
@ -908,12 +954,26 @@ function getYahooSingleDayData(Instrument) {
} else {
//console.warn("No timestamp series received for symbol: " + Instrument.Symbol);
//logWarning("No timestamp series received for symbol: " + Instrument.Symbol);
logWarning({ MSG: "No timestamp series received for symbol: " + Instrument.Symbol, response: response });
logWarning({ MSG: "getYahooSingleDayData - No timestamp series received for symbol: " + Instrument.Symbol, response: response });
}
if (dbUpdateNeeded) {
$.ajax({
type: "POST",
contentType: "application/json",
url: "SharePrices.aspx/UpdateInstrument?S=" + Instrument.Symbol,
dataType: "json",
data: JSON.stringify({ Symbol: Instrument.Symbol, GMTOffset: Instrument.GMTOffset, Currency: Instrument.Currency, CurrentPrice: Instrument.CurrentPrice, InstrumentType: Instrument.InstrumentType, TradeDayStart: Instrument.SingleDayStartDate, TradeDayEnd: Instrument.SingleDayEndDate }),
success: function (response) {
//console.info('UpdateInstrument success: ' + Instrument.Symbol + ' - ' + JSON.stringify(response));
},
failure: function (response) { console.error("getYahooSingleDayData - UpdateInstrument error: " + JSON.stringify(response)); }
});
}
} else {
//console.info("No data received for symbol: " + Instrument.Symbol);
//logInfo("No data received for symbol: " + Instrument.Symbol);
logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response });
logWarning({ MSG: "getYahooSingleDayData - No data received for symbol: " + Instrument.Symbol, response: response });
}
},
failure: function (response) {
@ -1014,6 +1074,34 @@ function submitNewDailyData(Instrument, NewData) {
}
function submitNewIntradayData(Instrument, NewData) {
//console.info("submitNewIntradayData");
//Submit the data in small chunks to avoid the HTTP payload getting too big
let noSubmitted = 0;
while (noSubmitted < NewData.length) {
let thisSubmission = [];
for (let x = noSubmitted; x < NewData.length && thisSubmission.length < 300; x++) {
thisSubmission.push(NewData[x]);
}
noSubmitted += thisSubmission.length;
//if (noSubmitted < NewData.length) {
// console.info("Need another round");
//}
$.ajax({
type: "POST",
contentType: "application/json",
url: "SharePrices.aspx/SubmitIntradayData?S=" + Instrument.Symbol,
dataType: "json",
data: JSON.stringify({ Symbol: Instrument.Symbol, DailyPrices: thisSubmission, GMTOffset: Instrument.GMTOffset, Currency: Instrument.Currency, CurrentPrice: Instrument.CurrentPrice, InstrumentType: Instrument.InstrumentType, TradeDayStart: Instrument.SingleDayStartDate, TradeDayEnd: Instrument.SingleDayEndDate }),
success: function (response) {
//console.info('SubmitIntradayData success: ' + Instrument.Symbol + ' - ' + JSON.stringify(response));
},
failure: function (response) { console.error("SubmitIntradayData error: " + JSON.stringify(response)); }
});
}
/*
$.ajax({
type: "POST",
contentType: "application/json",
@ -1026,6 +1114,7 @@ function submitNewIntradayData(Instrument, NewData) {
},
failure: function (response) { console.error("SubmitIntradayData error: " + JSON.stringify(response)); }
});
*/
//Add new data to Instrument
let currentSummary = {};
@ -1050,207 +1139,20 @@ function submitNewIntradayData(Instrument, NewData) {
}
function updateTables() {
//Only update the tables every 5 seconds (unless lastTablesUpdate has been reset)
//Only update the tables every x seconds
if (tablesUpdateTimings.updateNeeded && tablesUpdateTimings.lastUpdate + tablesUpdateTimings.timeBetweenUpdates < new Date().getTime()) {
tablesUpdateTimings.lastUpdate = new Date().getTime();
createHoldingsTable(Instruments, vars);
createAccountsTables();
//createHoldingsTable(Instruments, vars);
createAccountsTables(Instruments);
createAnalysisTable();
createIndexMarquee();
updateCurrenciesSpan();
updateHoldingsTables(Instruments, vars);
}
}
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, totalCashGBP: 0 };
accounts.push(a);
}
let h = {
instrumentName: instrument.InstrumentName,
symbol: instrument.Symbol,
instrumentType: instrument.InstrumentType,
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;
if (instrument.InstrumentType != 'CURRENCY') {
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;
if (instrument.InstrumentType != 'CURRENCY') {
a.totalGainGBP += h.gainGBP;
a.totalValueGBP += h.currentValueGBP;
}
}
}
if (instrument.InstrumentType == 'CURRENCY') {
//Don't add cash holdings - just add the cash value to the account
a.totalCashGBP += h.currentValueGBP;
} else {
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();
//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;
});
//let summaryTable = "<table class='accounts' id='accountsSummaryTable'><thead><tr><th>Account Name</th><th>No. Holdings</th><th>Total Cost (GBP)</th><th>Current Value</th><th>Total Gain</th></tr></thead><tbody>";
let summaryTable = "<table class='accounts' id='accountsSummaryTable'><thead><tr>" +
"<th>Account Name</th>" +
"<th>No. Holdings</th>" +
"<th>Total Cost (GBP)</th>" +
"<th>Total Gains (GBP)</th>" +
"<th>Total Losses (GBP)</th>" +
"<th>Current Investements Value</th>" +
"<th>NET Gain</th>" +
"<th>Cash Balance GBP</th>" +
"<th>Total Value GBP</th>" +
"</tr></thead><tbody>";
let accountsTables = ''
let tableIDs = ['accountsSummaryTable'];
let hideEmptyAccounts = $('#hideEmptyAccounts').prop('checked');
let altRow = 1;
for (let ax = 0; ax < accounts.length; ax++) {
let a = accounts[ax];
let accountGainsGBP = 0;
let accountLossesGBP = 0;
let accountValueGBP = 0;
let profitOrLoss = a.totalGainGBP < 0 ? 'loss' : 'profit';
if (a.holdings.length > 0) {
altRow = !altRow;
accountsTables += '<br>' + a.accountName + '<br>'
let tableID = 'accountTable' + ax;
tableIDs.push(tableID);
accountsTables += '<table class="accounts" id="' + tableID + '">' +
'<thead><tr>' +
'<th>Name</th>' +
'<th>Symbol</th>' +
'<th>Buy Date</th>' +
'<th>No Units</th>' +
'<th>Buy Price</th>' +
'<th>Current Price</th>' +
'<th>Book Cost</th>' +
'<th>Approx Book Cost £</th>' +
'<th>Current Value</th>' +
'<th>Current Value £</th>' +
'<th>Gain</th>' +
'<th>Gain £</th>' +
'<th>Gain %</th>' +
'</tr></thead><tbody>';
let accountAltRow = 1;
for (let hx = 0; hx < a.holdings.length; hx++) {
let h = a.holdings[hx];
accountAltRow = !accountAltRow;
let holdingProfitOrLoss = h.gain < 0 ? 'loss' : 'profit';
let holdingGBPProfitOrLoss = h.gainGBP < 0 ? 'loss' : 'profit';
if (h.gainGBP > 0) {
accountGainsGBP += h.gainGBP;
} else {
accountLossesGBP += h.gainGBP;
}
accountValueGBP += h.currentValueGBP;
accountsTables += '<tr' + (accountAltRow ? ' class="altShareRow"' : '') + '>' +
'<td>' + h.instrumentName + '</td>' +
'<td>' + h.symbol + '</td>' +
'<td>' + new Date(h.purchaseDate).yyyymmdd() + '</td>' +
'<td class="num">' + h.noUnits + '</td>' +
'<td class="num">' + formatAmount(h.purchasePricePerUnit, h.currency, 2) + '</td>' +
'<td class="num">' + formatAmount(h.currentPrice, h.currency, 2) + '</td>' +
'<td class="num">' + (h.currency == 'GBp' ? formatAmount(h.cost / 100, 'GBP', 2) : formatAmount(h.cost, h.currency, 2)) + '</td>' +
'<td class="num">' + formatAmount(h.costGBP, 'GBP', 2) + '</td>' +
'<td class="num">' + (h.currency == 'GBp' ? formatAmount(h.currentValue / 100, 'GBP', 2) : formatAmount(h.currentValue, h.currency, 2)) + '</td>' +
'<td class="num">' + formatAmount(h.currentValueGBP, 'GBP', 2) + '</td>' +
'<td class="num ' + holdingProfitOrLoss + '">' + formatAmount(h.gain, h.currency, 2) + '</td>' +
'<td class="num ' + holdingGBPProfitOrLoss + '">' + formatAmount(h.gainGBP, 'GBP', 2) + '</td>' +
//'<td class="num ' + holdingProfitOrLoss + '">' + ((h.gain / h.cost) * 100).toFixed(1) + '%</td>' +
'<td class="num ' + holdingGBPProfitOrLoss + '">' + ((h.gainGBP / h.costGBP) * 100).toFixed(1) + '%</td>' +
'</tr>';
}
//Add the Total Gains
accountAltRow = !accountAltRow;
accountsTables += '<tr' + (accountAltRow ? ' class="altShareRow"' : '') + '>' +
'<td class="num" colspan="11">Total Gains</td>' +
'<td class="num profit">' + formatAmount(accountGainsGBP, 'GBP', 2) + '</td>' +
'<td class="num">&nbsp;</td>' +
'</tr>';
//Add the Total Losses
accountAltRow = !accountAltRow;
accountsTables += '<tr' + (accountAltRow ? ' class="altShareRow"' : '') + '>' +
'<td class="num" colspan="11">Total Losses</td>' +
'<td class="num loss">' + formatAmount(accountLossesGBP, 'GBP', 2) + '</td>' +
'<td class="num">&nbsp;</td>' +
'</tr>';
//Add the NET Gains summary line
accountAltRow = !accountAltRow;
accountsTables += '<tr' + (accountAltRow ? ' class="altShareRow"' : '') + '>' +
'<td class="num" colspan="9">Account Value:</td>' +
'<td class="num ' + ((accountGainsGBP+accountLossesGBP) > 0 ? 'profit' : 'loss') + '">' + formatAmount(accountValueGBP, 'GBP', 2) + '</td>' +
'<td class="num">NET Gain</td>' +
'<td class="num ' + ((accountGainsGBP + accountLossesGBP) > 0 ? 'profit' : 'loss') + '">' + formatAmount(accountGainsGBP + accountLossesGBP, 'GBP', 2) + '</td>' +
'<td class="num">&nbsp;</td>' +
'</tr>';
accountsTables += '</tbody></table>';
}
//Add an entry to the summary table
if (a.totalValueGBP + a.totalCashGBP > 0 || !hideEmptyAccounts) {
summaryTable += "<tr" + (altRow ? ' class="altShareRow"' : '') + "><td>" + a.accountName + "</td>" +
"<td>" + a.holdings.length + "</td>" +
"<td class='num'>" + formatAmount(a.totalCostGBP || 0, 'GBP', 2) + "</td>" +
"<td class='num'>" + formatAmount(accountGainsGBP, 'GBP', 2) + "</td>" +
"<td class='num'>" + formatAmount(accountLossesGBP, 'GBP', 2) + "</td>" +
"<td class='num'>" + formatAmount(a.totalValueGBP || 0, 'GBP', 2) + "</td>" +
"<td class='num " + profitOrLoss + "'>" + formatAmount(a.totalGainGBP || 0, 'GBP', 2) + "</td>" +
"<td class='num'>" + formatAmount(a.totalCashGBP || 0, 'GBP', 2) + "</td>" +
"<td class='num'>" + formatAmount(a.totalValueGBP + a.totalCashGBP || 0, 'GBP', 2) + "</td>" +
"</tr>";
}
}
summaryTable += "</tbody></table>";
$("#accountsSummaryDiv").html(summaryTable + '<br>' + accountsTables);
}
function createAnalysisTable() {
function getHighestPriceBeforeDate(Instrument, maxDT) {
if (Instrument.DailyData) {
@ -1465,15 +1367,16 @@ function createIndexMarquee() {
//if (i.InstrumentType == 'INDEX' || i.InstrumentType == 'FUTURE') {
if (i.ShowInMarquee == 'A') {
let openOrClosed = Instruments.MarketIsOpen(i) == 1 ? 'open' : 'closed';
let label = '<a class="' + openOrClosed + '" target="_blank" href="https://uk.finance.yahoo.com/quote/' + i.Symbol + '">' + i.DisplayName + '</a>';
if (i.SingleDayPreviousClose) {
let percentChange = ((i.CurrentPrice / i.SingleDayPreviousClose) - 1) * 100;
if (percentChange < 0) {
t.push('<font class="' + openOrClosed + '">' + i.DisplayName + '<br/>' + i.CurrentPrice + ' </font><font class="loss">' + percentChange.toFixed(1) + '%</font>');
t.push('<font class="' + openOrClosed + '">' + label + '<br/>' + i.CurrentPrice + ' </font><font class="loss">' + percentChange.toFixed(1) + '%</font>');
} else {
t.push('<font class="' + openOrClosed + '">' + i.DisplayName + '<br/>' + i.CurrentPrice + ' </font><font class="profit">+' + percentChange.toFixed(1) + '%</font>');
t.push('<font class="' + openOrClosed + '">' + label + '<br/>' + i.CurrentPrice + ' </font><font class="profit">+' + percentChange.toFixed(1) + '%</font>');
}
} else {
t.push('<font class="' + openOrClosed + '">' + i.DisplayName + '<br/>' + i.CurrentPrice + '</font>');
t.push('<font class="' + openOrClosed + '">' + label + '<br/>' + i.CurrentPrice + '</font>');
}
}
}

View File

@ -0,0 +1,384 @@
'use strict';
import { formatAmount, getPercentCell } from "./SharePrices_Common.js";
/*
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, totalCashGBP: 0 };
accounts.push(a);
}
let h = {
instrumentName: instrument.InstrumentName,
symbol: instrument.Symbol,
instrumentType: instrument.InstrumentType,
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;
if (instrument.InstrumentType != 'CURRENCY') {
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;
if (instrument.InstrumentType != 'CURRENCY') {
a.totalGainGBP += h.gainGBP;
a.totalValueGBP += h.currentValueGBP;
}
}
}
if (instrument.InstrumentType == 'CURRENCY') {
//Don't add cash holdings - just add the cash value to the account
a.totalCashGBP += h.currentValueGBP;
} else {
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();
//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;
});
//let summaryTable = "<table class='accounts' id='accountsSummaryTable'><thead><tr><th>Account Name</th><th>No. Holdings</th><th>Total Cost (GBP)</th><th>Current Value</th><th>Total Gain</th></tr></thead><tbody>";
let summaryTable = "<table class='accounts' id='accountsSummaryTable'><thead><tr>" +
"<th>Account Name</th>" +
"<th>No. Holdings</th>" +
"<th>Total Cost (GBP)</th>" +
"<th>Total Gains (GBP)</th>" +
"<th>Total Losses (GBP)</th>" +
"<th>Current Investements Value</th>" +
"<th>NET Gain</th>" +
"<th>Cash Balance GBP</th>" +
"<th>Total Value GBP</th>" +
"</tr></thead><tbody>";
let accountsTables = ''
let tableIDs = ['accountsSummaryTable'];
let hideEmptyAccounts = $('#hideEmptyAccounts').prop('checked');
let altRow = 1;
for (let ax = 0; ax < accounts.length; ax++) {
let a = accounts[ax];
let accountGainsGBP = 0;
let accountLossesGBP = 0;
let accountValueGBP = 0;
let profitOrLoss = a.totalGainGBP < 0 ? 'loss' : 'profit';
if (a.holdings.length > 0) {
altRow = !altRow;
accountsTables += '<br>' + a.accountName + '<br>'
let tableID = 'accountTable' + ax;
tableIDs.push(tableID);
accountsTables += '<table class="accounts" id="' + tableID + '">' +
'<thead><tr>' +
'<th>Name</th>' +
'<th>Symbol</th>' +
'<th>Buy Date</th>' +
'<th>No Units</th>' +
'<th>Buy Price</th>' +
'<th>Current Price</th>' +
'<th>Book Cost</th>' +
'<th>Approx Book Cost £</th>' +
'<th>Current Value</th>' +
'<th>Current Value £</th>' +
'<th>Gain</th>' +
'<th>Gain £</th>' +
'<th>Gain %</th>' +
'</tr></thead><tbody>';
let accountAltRow = 1;
for (let hx = 0; hx < a.holdings.length; hx++) {
let h = a.holdings[hx];
accountAltRow = !accountAltRow;
let holdingProfitOrLoss = h.gain < 0 ? 'loss' : 'profit';
let holdingGBPProfitOrLoss = h.gainGBP < 0 ? 'loss' : 'profit';
if (h.gainGBP > 0) {
accountGainsGBP += h.gainGBP;
} else {
accountLossesGBP += h.gainGBP;
}
accountValueGBP += h.currentValueGBP;
accountsTables += '<tr' + (accountAltRow ? ' class="altShareRow"' : '') + '>' +
'<td>' + h.instrumentName + '</td>' +
'<td>' + h.symbol + '</td>' +
'<td>' + new Date(h.purchaseDate).yyyymmdd() + '</td>' +
'<td class="num">' + h.noUnits + '</td>' +
'<td class="num">' + formatAmount(h.purchasePricePerUnit, h.currency, 2) + '</td>' +
'<td class="num">' + formatAmount(h.currentPrice, h.currency, 2) + '</td>' +
'<td class="num">' + (h.currency == 'GBp' ? formatAmount(h.cost / 100, 'GBP', 2) : formatAmount(h.cost, h.currency, 2)) + '</td>' +
'<td class="num">' + formatAmount(h.costGBP, 'GBP', 2) + '</td>' +
'<td class="num">' + (h.currency == 'GBp' ? formatAmount(h.currentValue / 100, 'GBP', 2) : formatAmount(h.currentValue, h.currency, 2)) + '</td>' +
'<td class="num">' + formatAmount(h.currentValueGBP, 'GBP', 2) + '</td>' +
'<td class="num ' + holdingProfitOrLoss + '">' + formatAmount(h.gain, h.currency, 2) + '</td>' +
'<td class="num ' + holdingGBPProfitOrLoss + '">' + formatAmount(h.gainGBP, 'GBP', 2) + '</td>' +
//'<td class="num ' + holdingProfitOrLoss + '">' + ((h.gain / h.cost) * 100).toFixed(1) + '%</td>' +
'<td class="num ' + holdingGBPProfitOrLoss + '">' + ((h.gainGBP / h.costGBP) * 100).toFixed(1) + '%</td>' +
'</tr>';
}
//Add the Total Gains
accountAltRow = !accountAltRow;
accountsTables += '<tr' + (accountAltRow ? ' class="altShareRow"' : '') + '>' +
'<td class="num" colspan="11">Total Gains</td>' +
'<td class="num profit">' + formatAmount(accountGainsGBP, 'GBP', 2) + '</td>' +
'<td class="num">&nbsp;</td>' +
'</tr>';
//Add the Total Losses
accountAltRow = !accountAltRow;
accountsTables += '<tr' + (accountAltRow ? ' class="altShareRow"' : '') + '>' +
'<td class="num" colspan="11">Total Losses</td>' +
'<td class="num loss">' + formatAmount(accountLossesGBP, 'GBP', 2) + '</td>' +
'<td class="num">&nbsp;</td>' +
'</tr>';
//Add the NET Gains summary line
accountAltRow = !accountAltRow;
accountsTables += '<tr' + (accountAltRow ? ' class="altShareRow"' : '') + '>' +
'<td class="num" colspan="9">Account Value:</td>' +
'<td class="num ' + ((accountGainsGBP + accountLossesGBP) > 0 ? 'profit' : 'loss') + '">' + formatAmount(accountValueGBP, 'GBP', 2) + '</td>' +
'<td class="num">NET Gain</td>' +
'<td class="num ' + ((accountGainsGBP + accountLossesGBP) > 0 ? 'profit' : 'loss') + '">' + formatAmount(accountGainsGBP + accountLossesGBP, 'GBP', 2) + '</td>' +
'<td class="num">&nbsp;</td>' +
'</tr>';
accountsTables += '</tbody></table>';
}
//Add an entry to the summary table
if (a.totalValueGBP + a.totalCashGBP > 0 || !hideEmptyAccounts) {
summaryTable += "<tr" + (altRow ? ' class="altShareRow"' : '') + "><td>" + a.accountName + "</td>" +
"<td>" + a.holdings.length + "</td>" +
"<td class='num'>" + formatAmount(a.totalCostGBP || 0, 'GBP', 2) + "</td>" +
"<td class='num'>" + formatAmount(accountGainsGBP, 'GBP', 2) + "</td>" +
"<td class='num'>" + formatAmount(accountLossesGBP, 'GBP', 2) + "</td>" +
"<td class='num'>" + formatAmount(a.totalValueGBP || 0, 'GBP', 2) + "</td>" +
"<td class='num " + profitOrLoss + "'>" + formatAmount(a.totalGainGBP || 0, 'GBP', 2) + "</td>" +
"<td class='num'>" + formatAmount(a.totalCashGBP || 0, 'GBP', 2) + "</td>" +
"<td class='num'>" + formatAmount(a.totalValueGBP + a.totalCashGBP || 0, 'GBP', 2) + "</td>" +
"</tr>";
}
}
summaryTable += "</tbody></table>";
$("#accountsSummaryDiv").html(summaryTable + '<br>' + accountsTables);
}
*/
export function createAccountsTables(Instruments) {
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, totalCashGBP: 0 };
accounts.push(a);
}
let h = {
instrumentName: instrument.InstrumentName,
symbol: instrument.Symbol,
instrumentType: instrument.InstrumentType,
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;
if (instrument.InstrumentType != 'CURRENCY') {
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;
if (instrument.InstrumentType != 'CURRENCY') {
a.totalGainGBP += h.gainGBP;
a.totalValueGBP += h.currentValueGBP;
}
}
}
if (instrument.InstrumentType == 'CURRENCY') {
//Don't add cash holdings - just add the cash value to the account
a.totalCashGBP += h.currentValueGBP;
} else {
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();
//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;
});
//let summaryTable = "<table class='accounts' id='accountsSummaryTable'><thead><tr><th>Account Name</th><th>No. Holdings</th><th>Total Cost (GBP)</th><th>Current Value</th><th>Total Gain</th></tr></thead><tbody>";
let summaryTable = "<table class='accounts' id='accountsSummaryTable'><thead><tr>" +
"<th>Account Name</th>" +
"<th>No. Holdings</th>" +
"<th>Total Cost (GBP)</th>" +
"<th>Total Gains (GBP)</th>" +
"<th>Total Losses (GBP)</th>" +
"<th>Current Investements Value</th>" +
"<th>NET Gain</th>" +
"<th>Cash Balance GBP</th>" +
"<th>Total Value GBP</th>" +
"</tr></thead><tbody>";
let accountsTables = ''
let tableIDs = ['accountsSummaryTable'];
let hideEmptyAccounts = $('#hideEmptyAccounts').prop('checked');
let altRow = 1;
for (let ax = 0; ax < accounts.length; ax++) {
let a = accounts[ax];
let accountGainsGBP = 0;
let accountLossesGBP = 0;
let accountValueGBP = 0;
let profitOrLoss = a.totalGainGBP < 0 ? 'loss' : 'profit';
if (a.holdings.length > 0) {
altRow = !altRow;
accountsTables += '<br>' + a.accountName + '<br>'
let tableID = 'accountTable' + ax;
tableIDs.push(tableID);
accountsTables += '<table class="accounts" id="' + tableID + '">' +
'<thead><tr>' +
'<th>Name</th>' +
'<th>Symbol</th>' +
'<th>Buy Date</th>' +
'<th>No Units</th>' +
'<th>Buy Price</th>' +
'<th>Current Price</th>' +
'<th>Book Cost</th>' +
'<th>Approx Book Cost £</th>' +
'<th>Current Value</th>' +
'<th>Current Value £</th>' +
'<th>Gain</th>' +
'<th>Gain £</th>' +
'<th>Gain %</th>' +
'</tr></thead><tbody>';
let accountAltRow = 1;
for (let hx = 0; hx < a.holdings.length; hx++) {
let h = a.holdings[hx];
accountAltRow = !accountAltRow;
let holdingProfitOrLoss = h.gain < 0 ? 'loss' : 'profit';
let holdingGBPProfitOrLoss = h.gainGBP < 0 ? 'loss' : 'profit';
if (h.gainGBP > 0) {
accountGainsGBP += h.gainGBP;
} else {
accountLossesGBP += h.gainGBP;
}
accountValueGBP += h.currentValueGBP;
accountsTables += '<tr' + (accountAltRow ? ' class="altShareRow"' : '') + '>' +
'<td>' + h.instrumentName + '</td>' +
'<td>' + h.symbol + '</td>' +
'<td>' + new Date(h.purchaseDate).yyyymmdd() + '</td>' +
'<td class="num">' + h.noUnits + '</td>' +
'<td class="num">' + formatAmount(h.purchasePricePerUnit, h.currency, 2) + '</td>' +
'<td class="num">' + formatAmount(h.currentPrice, h.currency, 2) + '</td>' +
'<td class="num">' + (h.currency == 'GBp' ? formatAmount(h.cost / 100, 'GBP', 2) : formatAmount(h.cost, h.currency, 2)) + '</td>' +
'<td class="num">' + formatAmount(h.costGBP, 'GBP', 2) + '</td>' +
'<td class="num">' + (h.currency == 'GBp' ? formatAmount(h.currentValue / 100, 'GBP', 2) : formatAmount(h.currentValue, h.currency, 2)) + '</td>' +
'<td class="num">' + formatAmount(h.currentValueGBP, 'GBP', 2) + '</td>' +
'<td class="num ' + holdingProfitOrLoss + '">' + formatAmount(h.gain, h.currency, 2) + '</td>' +
'<td class="num ' + holdingGBPProfitOrLoss + '">' + formatAmount(h.gainGBP, 'GBP', 2) + '</td>' +
//'<td class="num ' + holdingProfitOrLoss + '">' + ((h.gain / h.cost) * 100).toFixed(1) + '%</td>' +
'<td class="num ' + holdingGBPProfitOrLoss + '">' + ((h.gainGBP / h.costGBP) * 100).toFixed(1) + '%</td>' +
'</tr>';
}
//Add the Total Gains
accountAltRow = !accountAltRow;
accountsTables += '<tr' + (accountAltRow ? ' class="altShareRow"' : '') + '>' +
'<td class="num" colspan="11">Total Gains</td>' +
'<td class="num profit">' + formatAmount(accountGainsGBP, 'GBP', 2) + '</td>' +
'<td class="num">&nbsp;</td>' +
'</tr>';
//Add the Total Losses
accountAltRow = !accountAltRow;
accountsTables += '<tr' + (accountAltRow ? ' class="altShareRow"' : '') + '>' +
'<td class="num" colspan="11">Total Losses</td>' +
'<td class="num loss">' + formatAmount(accountLossesGBP, 'GBP', 2) + '</td>' +
'<td class="num">&nbsp;</td>' +
'</tr>';
//Add the NET Gains summary line
accountAltRow = !accountAltRow;
accountsTables += '<tr' + (accountAltRow ? ' class="altShareRow"' : '') + '>' +
'<td class="num" colspan="9">Account Value:</td>' +
'<td class="num ' + ((accountGainsGBP + accountLossesGBP) > 0 ? 'profit' : 'loss') + '">' + formatAmount(accountValueGBP, 'GBP', 2) + '</td>' +
'<td class="num">NET Gain</td>' +
'<td class="num ' + ((accountGainsGBP + accountLossesGBP) > 0 ? 'profit' : 'loss') + '">' + formatAmount(accountGainsGBP + accountLossesGBP, 'GBP', 2) + '</td>' +
'<td class="num">&nbsp;</td>' +
'</tr>';
accountsTables += '</tbody></table>';
}
//Add an entry to the summary table
if (a.totalValueGBP + a.totalCashGBP > 0 || !hideEmptyAccounts) {
summaryTable += "<tr" + (altRow ? ' class="altShareRow"' : '') + "><td>" + a.accountName + "</td>" +
"<td>" + a.holdings.length + "</td>" +
"<td class='num'>" + formatAmount(a.totalCostGBP || 0, 'GBP', 2) + "</td>" +
"<td class='num'>" + formatAmount(accountGainsGBP, 'GBP', 2) + "</td>" +
"<td class='num'>" + formatAmount(accountLossesGBP, 'GBP', 2) + "</td>" +
"<td class='num'>" + formatAmount(a.totalValueGBP || 0, 'GBP', 2) + "</td>" +
"<td class='num " + profitOrLoss + "'>" + formatAmount(a.totalGainGBP || 0, 'GBP', 2) + "</td>" +
"<td class='num'>" + formatAmount(a.totalCashGBP || 0, 'GBP', 2) + "</td>" +
"<td class='num'>" + formatAmount(a.totalValueGBP + a.totalCashGBP || 0, 'GBP', 2) + "</td>" +
"</tr>";
}
}
summaryTable += "</tbody></table>";
$("#accountsSummaryDiv").html(summaryTable + '<br>' + accountsTables);
}

View File

@ -85,3 +85,12 @@ export function generateAxisBreaks(data, maxGapSize, breakSize) {
}
return breaks;
}
export 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;">&nbsp;</div>' +
'<div class="pcLabel" style="width:' + cellWidth + 'px">' + labelText + '</div>' +
'</td>';
}

View File

@ -1,7 +1,7 @@
'use strict';
import { formatAmount } from "./SharePrices_Common.js";
import { formatAmount, getPercentCell } from "./SharePrices_Common.js";
/*
export function createHoldingsTable(Instruments, vars) {
function getHoldings() {
function aggregateHoldings(i) {
@ -302,12 +302,17 @@ export function createHoldingsTable(Instruments, vars) {
$('#holdingsTableDiv').html(tbl);
$("#tblMainHoldings").tablesorter({ ignoreCase: false });
$("#tblMainHoldings").trigger("sorton", Cookies.get('sortHoldings'));
//let sortOrder = Cookies.get('sortHoldings');
let sortOrder = JSON.parse(Cookies.get('sortHoldings'));
$("#tblMainHoldings").trigger("sorton", [sortOrder]);
$('#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 });
}
//console.info({MSG: "Saving sortHolding cookie", Cookie: event.target.config.sortList});
Cookies.set('sortHoldings', event.target.config.sortList, { 'sameSite': 'strict' });
});
}
@ -354,14 +359,16 @@ export function createHoldingsTable(Instruments, vars) {
//$("#" + 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;">&nbsp;</div>' +
'<div class="pcLabel" style="width:' + cellWidth + 'px">' + labelText + '</div>' +
'</td>';
}
//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;">&nbsp;</div>' +
// '<div class="pcLabel" style="width:' + cellWidth + 'px">' + labelText + '</div>' +
// '</td>';
//}
function profitOrLoss(value) {
return value >= 0 ? "profit" : "loss";
}
@ -495,35 +502,6 @@ export function createHoldingsTable(Instruments, vars) {
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:&nbsp;</span>' + '<span style="font-size: large">' + formattedAmount + '</span>&nbsp;';
holdingsHTML += '<span style="font-size: small;"><span class="' + (vars.previousClose < totalValueGBP ? 'profit">+' : 'loss">') + formatAmount(totalValueGBP - vars.previousClose, "GBP", 0) + '</span>&nbsp;(d)';
holdingsHTML += '&nbsp;/&nbsp;<span class="' + (vars.previousWeekClose < totalValueGBP ? 'profit">+' : 'loss">') + formatAmount(totalValueGBP - vars.previousWeekClose, "GBP", 0) + '</span>&nbsp;(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>";
@ -589,3 +567,645 @@ function createCategoryAggregator() {
};
return aggregator;
}
*/
export async function updateHoldingsTables(instruments, vars) {
createNewHoldingsTable(instruments, vars);
createAggregatedHoldingsTable(instruments, vars);
createWatchlistTable(instruments);
}
async function createNewHoldingsTable(instruments, vars) {
const noColumns = 16;
function createNewHoldingsTable() {
let table = $('#newHoldingsTable');
if (table.length == 0) {
let tblDef = '<table id="newHoldingsTable" class="mainHoldings">' +
getHeaderRow() +
'<tr id="newHoldingsTable_EndSpacer" class="mainHoldingsSpacer"><td colspan="' + noColumns.toString() + '">&nbsp;</td></tr>' +
'<tr id="newHoldingsTable_InvestmentsGrandTotals"><td colspan="' + noColumns.toString() + '">New Holdings Table Grand Totals Row</td></tr>' +
'<tr id="newHoldingsTable_CashTotal"><td colspan="' + noColumns.toString() + '">New Holdings Cash Total Row</td></tr>' +
'<tr id="newHoldingsTable_GrandTotal"><td colspan="' + noColumns.toString() + '">New Holdings Grand Total Row</td></tr>' +
'</table>';
$('#newHoldingsTableDiv').html(tblDef);
}
}
function createTaxBucketSubTable(taxBucket) {
let subtableID = 'taxBucket_' + taxBucket.replaceAll(' ', '_');
//let subtable = $('#' + subtableID);
let subtable = $('#' + subtableID + '_Summary');
if (subtable.length == 0) {
//let rowsDef = '<tr id="' + subtableID + '"><td colspan="' + noColumns.toString() + '">' + taxBucket + '</td></tr>\n' +
//let rowsDef = getHeaderRow() +
//let dt = (new Date()).yyyymmddhhmmss();
//let rowsDef = '<tr id="' + subtableID + '_Spacer"><td colspan="' + noColumns.toString() + '" class="mainHoldingsSpacer">&nbsp;' + dt + '</td></tr>\n' + //Spacer row
let rowsDef = '<tr id="' + subtableID + '_Summary" class="holdingsSubtotalRow"><td>&nbsp;</td></tr>'; //Sub Table Summary Row
//$(rowsDef).insertBefore('#newHoldingsTable_InvestmentsGrandTotals');
$(rowsDef).insertBefore('#newHoldingsTable_EndSpacer');
}
}
function getHeaderRow() {
return '<tr>' +
'<th>Account</th > ' +
'<th>Type</th > ' +
'<th>Name</th > ' +
'<th>Symbol</th > ' +
'<th>Ccy</th > ' +
'<th>No Units</th > ' +
'<th>Avg Buy<br/>Price</th > ' +
'<th>Previous<br/>Close</th > ' +
'<th>Current<br/>Price</th > ' +
'<th>Base Cost</th > ' +
'<th>Current Value</th > ' +
'<th>Gain</th > ' +
'<th>Gain %</th > ' +
'<th>Allocation</th > ' +
'<th>Today&apos;s<br/>Gain £</th>' +
'<th>Today&apos;s<br/>Gain %</th > ' +
'</tr>'
}
function getContentRow(row, instrument, scaledAllocation, allocationPercent) {
let openOrClosed = instruments.MarketIsOpen(instrument) == 1 ? 'open' : 'closed';
let rowID = 'newHoldingsRow_' + vars.currentHoldingsRows.rowID(row);
let type = row.InstrumentType == 'CURRENCY' ? '$' : row.InstrumentType.substring(0, 1);
let totalProfitOrLoss = row.UnrealisedGainGBP < 0 ? 'loss' : 'profit';
let todaysGainGBP = 0;
let todaysGainPct = 0;
let todaysProfitOrLoss = 'profit';
if (instrument.SingleDayPreviousClose) {
todaysGainGBP = (row.CurrentPriceGBP - (instrument.SingleDayPreviousClose / row.CurrentExchangeRate)) * row.NoUnits;
todaysGainPct = ((row.CurrentPriceGBP / (instrument.SingleDayPreviousClose / row.CurrentExchangeRate)) - 1) * 100;
todaysProfitOrLoss = todaysGainGBP < 0 ? 'loss' : 'profit';
}
let cryptoHoldingClass = instrument.InstrumentType == 'CRYPTOCURRENCY' ? ' cryptoHoldingRow' : '';
return '<tr id="' + rowID + '" class="holdingsRow ' + openOrClosed + cryptoHoldingClass + '">' +
'<td>' + row.AccountName + '</td>' +
'<td>' + type + '</td>' +
'<td>' + row.InstrumentName + '</td>' +
//'<td>' + row.Symbol + '</td>' +
'<td><a target="_blank" href="https://uk.finance.yahoo.com/quote/' + instrument.Symbol + '">' + instrument.Symbol + '</a>' + '</td>' +
'<td>' + row.InstrumentCurrency + '</td>' +
'<td class="num">' + row.NoUnits.autoScale() + '</td>' +
'<td class="num">' + formatAmount(row.AvgPriceGBP, 'GBP', 2) + '</td>' +
'<td class="num">' + formatAmount(instrument.SingleDayPreviousClose / row.CurrentExchangeRate, 'GBP', 2) + '</td>' +
'<td class="num">' + formatAmount(row.CurrentPriceGBP, 'GBP', 2) + '</td>' +
'<td class="num">' + formatAmount(row.BaseCostGBP, 'GBP', 0) + '</td>' +
'<td class="num">' + formatAmount(row.CurrentValueGBP, 'GBP', 0) + '</td>' +
'<td class="num ' + totalProfitOrLoss + '">' + formatAmount(row.UnrealisedGainGBP, 'GBP', 0) + '</td>' +
'<td class="num ' + totalProfitOrLoss + '">' + formatAmount(row.UnrealisedGainPct, '', 1) + '%</td>' + //Gain %
//'<td>' + '&nbsp;' + '</td>' + //Allocation
getPercentCell('#280ea6' /*'#3f2d95'*/, 70, scaledAllocation, allocationPercent.toFixed(1) + '%') + //Allocation
'<td class="num ' + todaysProfitOrLoss + '">' + ((todaysGainGBP == 0) ? '&nbsp;' : formatAmount(todaysGainGBP, 'GBP', 0)) + '</td>' + //Today's Gain £
'<td class="num ' + todaysProfitOrLoss + '">' + ((todaysGainPct == 0) ? '&nbsp;' : formatAmount(todaysGainPct, '', 1) + '%') + '</td>' + //Today's Gain %
'</tr>'
}
function updateTaxBucketSubtotals(taxBucket, baseCostGBP, currentValueGBP, todaysGain) {
let subtableID = 'taxBucket_' + taxBucket.replaceAll(' ', '_');
let rowID = subtableID + '_Summary';
let profitOrLoss = currentValueGBP < baseCostGBP ? 'loss' : 'profit';
let rowDef = '<tr id="' + subtableID + '_Summary" class="holdingsSubtotal">' +
'<td colspan="9" class="num">' + taxBucket + ' Sutotal</td>' +
'<td class="num">' + formatAmount(baseCostGBP, 'GBP', 2) + '</td>' + //BaseCost
'<td class="num">' + formatAmount(currentValueGBP, 'GBP', 2) + '</td>' + //CurrentValue
'<td class="num ' + profitOrLoss + '">' + formatAmount(currentValueGBP - baseCostGBP, 'GBP', 0) + '</td>' + //GainGBP
'<td class="num ' + profitOrLoss + '">' + formatAmount((currentValueGBP - baseCostGBP) / baseCostGBP * 100, '', 1) + '%</td>' + //Gain%
'<td>&nbsp;</td>' +
'<td class="num ' + (todaysGain < 0 ? 'loss' : 'profit') + '">' + (todaysGain == 0 ? '&nbsp;' : formatAmount(todaysGain, 'GBP', 0)) + '</td>' +
'<td>&nbsp;</td>' +
'</tr>';
$('#' + rowID).replaceWith(rowDef);
}
function updateInvestmentsGrandTotals(totalBaseCostGBP, totalCurrentValueGBP, todaysGain) {
let profitOrLoss = totalCurrentValueGBP < totalBaseCostGBP ? 'loss' : 'profit';
let rowID = 'newHoldingsTable_InvestmentsGrandTotals';
let rowDef = '<tr id="' + rowID + '">' +
'<td colspan="9" class="num holdingsGrandTotal">Investments Total</td>' +
'<td class="num">' + formatAmount(totalBaseCostGBP, 'GBP', 0) + '</td>' + //BaseCost
'<td class="num">' + formatAmount(totalCurrentValueGBP, 'GBP', 0) + '</td>' + //CurrentValue
'<td class="num ' + profitOrLoss + '">' + formatAmount(totalCurrentValueGBP - totalBaseCostGBP, 'GBP', 0) + '</td>' + //GainGBP
'<td class="num ' + profitOrLoss + '">' + formatAmount((totalCurrentValueGBP - totalBaseCostGBP) / totalBaseCostGBP * 100, '', 1) + '%</td>' + //Gain%
'<td>&nbsp;</td>' +
'<td class="num ' + (todaysGain < 0 ? 'loss' : 'profit') + '">' + (todaysGain == 0 ? '&nbsp;' : formatAmount(todaysGain, 'GBP', 0)) + '</td>' +
'<td>&nbsp;</td>' +
'</tr>';
$('#' + rowID).replaceWith(rowDef);
}
function updateCashTotal(totalCashGBP) {
let rowID = 'newHoldingsTable_CashTotal';
//let rowDef = '<tr id="' + rowID + '" class="holdingsGrandTotal">' +
let rowDef = '<tr id="' + rowID + '">' +
'<td colspan="9" class="num holdingsGrandTotal">Cash Total</td>' +
'<td>&nbsp;</td>' +
'<td class="num">' + formatAmount(totalCashGBP, 'GBP', 0) + '</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'</tr>';
$('#' + rowID).replaceWith(rowDef);
}
function updateGrandTotal(grandTotalGBP) {
let rowID = 'newHoldingsTable_GrandTotal';
let rowDef = '<tr id="' + rowID + '">' +
'<td colspan="9" class="num holdingsGrandTotal">Grand Total</td>' +
'<td>&nbsp;</td>' +
'<td class="num">' + formatAmount(grandTotalGBP, 'GBP', 0) + '</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'</tr>';
$('#' + rowID).replaceWith(rowDef);
}
function updateTableRow(row, newContent) {
let rowID = 'newHoldingsRow_' + vars.currentHoldingsRows.rowID(row).replaceAll('.', '\\.');
let tr = $('#' + rowID);
if (tr.length > 0) {
//Row exists... update content
$(tr).replaceWith(newContent);
} else {
//Need to add row
createNewHoldingsTable();
createTaxBucketSubTable(row.TaxBucket);
$(newContent).insertBefore('#taxBucket_' + row.TaxBucket.replaceAll(' ', '_') + '_Summary');
}
//Add the highlighted class so the updated row will flash
$('#' + rowID).addClass("highlighted");
}
let holdings = {};
try {
holdings = await $.ajax({
type: "POST",
contentType: "application/json",
url: "SharePrices.aspx/GetCurrentHoldings"
});
}
catch (error) {
console.error({ MSG: "Error getting current holdings", Error: error });
}
//Save the current holdings for use elsewehere
vars.currentHoldings = holdings;
//Calculate totals before creating table rows
let totalCashGBP = 0;
let totalBaseCost = 0;
let totalCurrentValue = 0;
let maxCurrentValue = 0;
for (let rowNo = 0; rowNo < holdings.length; rowNo++) {
let r = holdings[rowNo];
if(r.InstrumentType == 'CURRENCY') {
totalCashGBP += r.CurrentValueGBP;
} else {
totalBaseCost += r.BaseCostGBP;
totalCurrentValue += r.CurrentValueGBP;
if (r.CurrentValueGBP > maxCurrentValue) maxCurrentValue = r.CurrentValueGBP;
}
}
let maxAllocationPercent = (maxCurrentValue / totalCurrentValue) * 100;
let allocationScaleFactor = 100 / maxAllocationPercent;
//Create the holdings table rows
let currentTaxBucket = '';
let bucketBaseCost = 0;
let bucketCurrentValue = 0;
let bucketTodaysGain = 0;
let totalTodaysGain = 0;
for (let rowNo = 0; rowNo < holdings.length; rowNo++) {
let r = holdings[rowNo];
if(r.InstrumentType != 'CURRENCY') {
let instrument = instruments.GetSymbol(r.Symbol);
let allocationPercent = r.CurrentValueGBP / totalCurrentValue * 100;
let scaledAllocation = allocationScaleFactor * allocationPercent;
if (r.TaxBucket != currentTaxBucket) {
if (currentTaxBucket != '') {
//Update the subtotal row for the previous tax bucket
updateTaxBucketSubtotals(currentTaxBucket, bucketBaseCost, bucketCurrentValue, bucketTodaysGain);
}
currentTaxBucket = r.TaxBucket;
bucketBaseCost = 0;
bucketCurrentValue = 0;
bucketTodaysGain = 0;
}
bucketBaseCost += r.BaseCostGBP;
bucketCurrentValue += r.CurrentValueGBP;
let todaysGain = instrument.SingleDayPreviousClose ? (r.CurrentPriceGBP - (instrument.SingleDayPreviousClose / r.CurrentExchangeRate)) * r.NoUnits : 0;
bucketTodaysGain += todaysGain;
totalTodaysGain += todaysGain;
//Has this row changed?
let currentRow = vars.currentHoldingsRows.GetRow(r);
let newContent = getContentRow(r, instrument, scaledAllocation, allocationPercent);
if (!currentRow) {
vars.currentHoldingsRows.Data.push({ attributes: r, content: newContent });
updateTableRow(r, newContent);
} else if (currentRow.content != newContent) {
currentRow.attributes = r;
currentRow.content = newContent;
updateTableRow(r, newContent);
}
}
/*
TaxBucket
AccountName
IsTaxable
InstrumentType
Symbol
InstrumentName
InstrumentCurrency
NoUnits
BaseCostGBP
AvgPriceGBP
CurrentPriceGBP
CurrentValueGBP
UnrealisedGainGBP
*/
}
let totalValueGBP = totalCurrentValue + totalCashGBP;
updateTaxBucketSubtotals(currentTaxBucket, bucketBaseCost, bucketCurrentValue, bucketTodaysGain);
updateInvestmentsGrandTotals(totalBaseCost, totalCurrentValue, totalTodaysGain);
updateCashTotal(totalCashGBP);
updateGrandTotal(totalValueGBP);
//Show or hide the Tax Bucket subtotals
let subtotalsDisplay = Cookies.get('showSubtotals');
if (subtotalsDisplay == 'true') {
$("#newHoldingsTable tr.holdingsSubtotal").show();
} else {
$("#newHoldingsTable tr.holdingsSubtotal").hide();
}
//Show or hide cryptocurrency holdings
let cryptoDisplay = Cookies.get('showCryptoHoldings');
if (cryptoDisplay == 'true') {
$("#newHoldingsTable tr.cryptoHoldingRow").show();
} else {
$("#newHoldingsTable tr.cryptoHoldingRow").hide();
}
//Remove the highlight from all rows/cells
$("#newHoldingsTable").find(".highlighted").removeClass("highlighted", 1200);
//Update the daily holdings value table in the DB
if (totalValueGBP > 0 && 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:&nbsp;</span>' + '<span style="font-size: large">' + formattedAmount + '</span>&nbsp;';
holdingsHTML += '<span style="font-size: small;"><span class="' + (vars.previousClose < totalValueGBP ? 'profit">+' : 'loss">') + formatAmount(totalValueGBP - vars.previousClose, "GBP", 0) + '</span>&nbsp;(d)';
holdingsHTML += '&nbsp;/&nbsp;<span class="' + (vars.previousWeekClose < totalValueGBP ? 'profit">+' : 'loss">') + formatAmount(totalValueGBP - vars.previousWeekClose, "GBP", 0) + '</span>&nbsp;(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)); }
});
}
}
async function createAggregatedHoldingsTable(instruments, vars) {
const noColumns = 15;
function createAggHoldingsTable() {
let table = $('#aggHoldingsTable');
if (table.length == 0) {
let tblDef = '<table id="aggHoldingsTable" class="mainHoldings">' +
getHeaderRow() +
'<tr id="aggHoldingsTable_EndSpacer" class="mainHoldingsSpacer"><td colspan="' + noColumns.toString() + '">&nbsp;</td></tr>' +
'<tr id="aggHoldingsTable_InvestmentsGrandTotals"><td colspan="' + noColumns.toString() + '">Aggregated Holdings Table Grand Totals Row</td></tr>' +
'<tr id="aggHoldingsTable_CashTotal"><td colspan="' + noColumns.toString() + '">Aggregated Holdings Cash Total Row</td></tr>' +
'<tr id="aggHoldingsTable_GrandTotal"><td colspan="' + noColumns.toString() + '">Aggregated Holdings Grand Total Row</td>' +
'</tr>' +
'</table>';
$('#aggHoldingsTableDiv').html(tblDef);
}
}
function getHeaderRow() {
return '<tr>' +
'<th>Type</th > ' +
'<th>Name</th > ' +
'<th>Symbol</th > ' +
'<th>Ccy</th > ' +
'<th>No Units</th > ' +
'<th>Avg Buy<br/>Price</th > ' +
'<th>Previous<br/>Close</th > ' +
'<th>Current<br/>Price</th > ' +
'<th>Base Cost</th > ' +
'<th>Current Value</th > ' +
'<th>Gain</th > ' +
'<th>Gain %</th > ' +
'<th>Allocation</th > ' +
'<th>Today&apos;s<br/>Gain £</th>' +
'<th>Today&apos;s<br/>Gain %</th > ' +
'</tr>'
}
function getContentRow(row, instrument, scaledAllocation, allocationPercent) {
let openOrClosed = instruments.MarketIsOpen(instrument) == 1 ? 'open' : 'closed';
let rowID = 'aggHoldingsRow_' + vars.currentAggHoldingsRows.rowID(row);
let type = row.InstrumentType == 'CURRENCY' ? '$' : row.InstrumentType.substring(0, 1);
let totalProfitOrLoss = row.UnrealisedGainGBP < 0 ? 'loss' : 'profit';
let todaysGainGBP = 0;
let todaysGainPct = 0;
let todaysProfitOrLoss = 'profit';
if (instrument.SingleDayPreviousClose) {
todaysGainGBP = (row.CurrentPriceGBP - (instrument.SingleDayPreviousClose / row.CurrentExchangeRate)) * row.NoUnits;
todaysGainPct = ((row.CurrentPriceGBP / (instrument.SingleDayPreviousClose / row.CurrentExchangeRate)) - 1) * 100;
todaysProfitOrLoss = todaysGainGBP < 0 ? 'loss' : 'profit';
}
return '<tr id="' + rowID + '" class="holdingsRow ' + openOrClosed + '">' +
'<td>' + type + '</td>' +
'<td>' + row.InstrumentName + '</td>' +
//'<td>' + row.Symbol + '</td>' +
'<td><a target="_blank" href="https://uk.finance.yahoo.com/quote/' + instrument.Symbol + '">' + instrument.Symbol + '</a>' + '</td>' +
'<td>' + row.InstrumentCurrency + '</td>' +
'<td class="num">' + row.NoUnits.autoScale() + '</td>' +
'<td class="num">' + formatAmount(row.AvgPriceGBP, 'GBP', 2) + '</td>' +
'<td class="num">' + formatAmount(instrument.SingleDayPreviousClose / row.CurrentExchangeRate, 'GBP', 2) + '</td>' +
'<td class="num">' + formatAmount(row.CurrentPriceGBP, 'GBP', 2) + '</td>' +
'<td class="num">' + formatAmount(row.BaseCostGBP, 'GBP', 0) + '</td>' +
'<td class="num">' + formatAmount(row.CurrentValueGBP, 'GBP', 0) + '</td>' +
'<td class="num ' + totalProfitOrLoss + '">' + formatAmount(row.UnrealisedGainGBP, 'GBP', 0) + '</td>' +
'<td class="num ' + totalProfitOrLoss + '">' + formatAmount(row.UnrealisedGainPct, '', 1) + '%</td>' + //Gain %
//'<td>' + '&nbsp;' + '</td>' + //Allocation
getPercentCell('#280ea6' /*'#3f2d95'*/, 70, scaledAllocation, allocationPercent.toFixed(1) + '%') + //Allocation
'<td class="num ' + todaysProfitOrLoss + '">' + ((todaysGainGBP == 0) ? '&nbsp;' : formatAmount(todaysGainGBP, 'GBP', 0)) + '</td>' + //Today's Gain £
'<td class="num ' + todaysProfitOrLoss + '">' + ((todaysGainPct == 0) ? '&nbsp;' : formatAmount(todaysGainPct, '', 1) + '%') + '</td>' + //Today's Gain %
'</tr>'
}
function updateInvestmentsGrandTotals(totalBaseCostGBP, totalCurrentValueGBP, todaysGain) {
let profitOrLoss = totalCurrentValueGBP < totalBaseCostGBP ? 'loss' : 'profit';
let rowID = 'aggHoldingsTable_InvestmentsGrandTotals';
let rowDef = '<tr id="' + rowID + '">' +
'<td colspan="8" class="num holdingsGrandTotal">Investments Total</td>' +
'<td class="num">' + formatAmount(totalBaseCostGBP, 'GBP', 0) + '</td>' + //BaseCost
'<td class="num">' + formatAmount(totalCurrentValueGBP, 'GBP', 0) + '</td>' + //CurrentValue
'<td class="num ' + profitOrLoss + '">' + formatAmount(totalCurrentValueGBP - totalBaseCostGBP, 'GBP', 0) + '</td>' + //GainGBP
'<td class="num ' + profitOrLoss + '">' + formatAmount((totalCurrentValueGBP - totalBaseCostGBP) / totalBaseCostGBP * 100, '', 1) + '%</td>' + //Gain%
'<td>&nbsp;</td>' +
'<td class="num ' + (todaysGain < 0 ? 'loss' : 'profit') + '">' + (todaysGain == 0 ? '&nbsp;' : formatAmount(todaysGain, 'GBP', 0)) + '</td>' +
'<td>&nbsp;</td>' +
'</tr>';
$('#' + rowID).replaceWith(rowDef);
}
function updateCashTotal(totalCashGBP) {
let rowID = 'aggHoldingsTable_CashTotal';
let rowDef = '<tr id="' + rowID + '">' +
'<td colspan="8" class="num holdingsGrandTotal">Cash Total</td>' +
'<td>&nbsp;</td>' +
'<td class="num">' + formatAmount(totalCashGBP, 'GBP', 0) + '</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'</tr>';
$('#' + rowID).replaceWith(rowDef);
}
function updateGrandTotal(grandTotalGBP) {
let rowID = 'aggHoldingsTable_GrandTotal';
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 lastUpdatedCell = '<td id="aggHoldingsLastUpdated" class="highlighted" colspan="6">Updated: ' + new Date().yyyymmddhhmmss() + lSF + '</td>';
let rowDef = '<tr id="' + rowID + '">' +
//'<td colspan="8" class="num">Grand Total</td>' +
'<td id="aggHoldingsLastUpdated" class="highlighted" colspan="7">Updated: ' + new Date().yyyymmddhhmmss() + lSF + '</td>' +
'<td class="num holdingsGrandTotal">Grand Total</td>' +
'<td>&nbsp;</td>' +
'<td class="num">' + formatAmount(grandTotalGBP, 'GBP', 0) + '</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'<td>&nbsp;</td>' +
'</tr>';
$('#' + rowID).replaceWith(rowDef);
}
function updateTableRow(row, newContent) {
let rowID = 'aggHoldingsRow_' + vars.currentAggHoldingsRows.rowID(row).replaceAll('.', '\\.');
let tr = $('#' + rowID);
if (tr.length > 0) {
//Row exists... update content
$(tr).replaceWith(newContent);
} else {
//Need to add row
createAggHoldingsTable();
//$(newContent).insertBefore('#aggHoldingsTable_InvestmentsGrandTotals');
$(newContent).insertBefore('#aggHoldingsTable_EndSpacer');
}
//Add the highlighted class so the updated row will flash
$('#' + rowID).addClass("highlighted");
}
let holdings = {};
try {
holdings = await $.ajax({
type: "POST",
contentType: "application/json",
url: "SharePrices.aspx/GetAggregatedCurrentHoldings"
});
}
catch (error) {
console.error({ MSG: "Error getting aggregated current holdings", Error: error });
}
//Calculate totals before creating table rows
let totalCashGBP = 0;
let totalBaseCost = 0;
let totalCurrentValue = 0;
let maxCurrentValue = 0;
for (let rowNo = 0; rowNo < holdings.length; rowNo++) {
let r = holdings[rowNo];
if (r.InstrumentType == 'CURRENCY') {
totalCashGBP += r.CurrentValueGBP;
} else {
totalBaseCost += r.BaseCostGBP;
totalCurrentValue += r.CurrentValueGBP;
if (r.CurrentValueGBP > maxCurrentValue) maxCurrentValue = r.CurrentValueGBP;
}
}
let maxAllocationPercent = (maxCurrentValue / totalCurrentValue) * 100;
let allocationScaleFactor = 100 / maxAllocationPercent;
//Create the holdings table rows
let totalTodaysGain = 0;
for (let rowNo = 0; rowNo < holdings.length; rowNo++) {
let r = holdings[rowNo];
if (r.InstrumentType != 'CURRENCY') {
let instrument = instruments.GetSymbol(r.Symbol);
let allocationPercent = r.CurrentValueGBP / totalCurrentValue * 100;
let scaledAllocation = allocationScaleFactor * allocationPercent;
let todaysGain = instrument.SingleDayPreviousClose ? (r.CurrentPriceGBP - (instrument.SingleDayPreviousClose / r.CurrentExchangeRate)) * r.NoUnits : 0;
totalTodaysGain += todaysGain;
//Has this row changed?
let currentRow = vars.currentAggHoldingsRows.GetRow(r);
let newContent = getContentRow(r, instrument, scaledAllocation, allocationPercent);
if (!currentRow) {
vars.currentAggHoldingsRows.Data.push({ attributes: r, content: newContent });
updateTableRow(r, newContent);
} else if (currentRow.content != newContent) {
currentRow.attributes = r;
currentRow.content = newContent;
updateTableRow(r, newContent);
}
}
/*
InstrumentType
Symbol
InstrumentName
InstrumentCurrency
NoUnits
BaseCostGBP
AvgPriceGBP
CurrentPriceGBP
CurrentValueGBP
UnrealisedGainGBP
*/
}
let totalValueGBP = totalCurrentValue + totalCashGBP;
updateInvestmentsGrandTotals(totalBaseCost, totalCurrentValue, totalTodaysGain);
updateCashTotal(totalCashGBP);
updateGrandTotal(totalValueGBP);
/*
//Show or hide the Tax Bucket subtotals
let subtotalsDisplay = Cookies.get('showSubtotals');
if (subtotalsDisplay == 'true') {
$("#newHoldingsTable tr.holdingsSubtotal").show();
} else {
$("#newHoldingsTable tr.holdingsSubtotal").hide();
}
*/
//Remove the highlight from all rows/cells
$("#aggHoldingsTable").find(".highlighted").removeClass("highlighted", 1200);
}
function createWatchlistTable(Instruments) {
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");
}
}
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&apos;s Gain</th>' +
'<th>Today&apos;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' });
});
}
let altRow = 1;
for (let n = 0; n < Instruments.Data.length; n++) {
if (Instruments.Data[n].WatchlistID) {
altRow = !altRow;
let wi = Instruments.Data[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 class="' + openOrClosed + '">' + wi.DisplayName + '</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);
}
}
}
//Remove the highlight from all rows/cells
$("#tbWatchlist").find(".highlighted").removeClass("highlighted", 1200);
}

View File

@ -0,0 +1,117 @@
'use strict';
import { formatAmount, getPercentCell } from "./SharePrices_Common.js";
export async function updateLedgerTable(instruments, vars) {
const noColumns = 15;
function createLedgerTable() {
let table = $('#ledgerTable');
//if (table.length == 0) {
let tblDef = '<table id="ledgerTable" class="ledger">' +
'<thead>' + getHeaderRow() + '</thead>' +
'<tbody></tbody>' +
'</table>';
$('#ledgerDiv').html(tblDef);
//}
}
function getHeaderRow() {
return '<tr>' +
'<th>Trade DT</th>' +
'<th>Tax Bucket</th>' +
'<th>Account</th>' +
'<th>Symbol</th>' +
'<th>Instrument Name</th>' +
//'<th>Instrument Currency</th>' +
'<th>Action<br/>Type</th>' +
//'<th>No Units</th>' +
//'<th>Price Per Unit GBP</th>' +
'<th>Units</th>' +
'<th>Trade<br/>Value</th>' +
'<th>New Pool<br/>Total Units</th>' +
'<th>New Pool<br/>Base Cost</th>' +
'<th>Realised<br/>Gain</th>' +
'<th>Is Final<br/>Position</th>' +
'<th>Notes</th>' +
'</tr>'
}
function getContentRow(row) {
let rowID = 'ledgerRow_' + row.TradeLedgerID.toString();
return '<tr id="' + rowID + '" class="ledgerRow">' +
'<td>' + (new Date(row.TradeDT)).yyyymmddhhmm() + '</td>' +
'<td>' + row.TaxBucket + '</td>' +
'<td>' + row.AccountName + '</td>' +
'<td>' + row.Symbol + '</td>' +
'<td>' + row.InstrumentName + '</td>' +
//'<td>' + row.InstrumentCurrency + '</td>' +
'<td>' + row.ActionType + '</td>' +
//'<td class="num">' + row.NoUnits.autoScale() + '</td>' +
//'<td class="num">' + formatAmount(row.PricePerUnitGBP, 'GBP', 2) + '</td>' +
'<td class="num">' + row.NoUnits.autoScale() + ' @ ' + formatAmount(row.PricePerUnitGBP, 'GBP', 2) + '</td>' +
'<td class="num">' + formatAmount(row.TradeValueGBP, 'GBP', 2) + '</td>' +
'<td class="num">' + row.NewPoolTotalUnits.autoScale() + '</td>' +
'<td class="num">' + formatAmount(row.NewPoolBaseCostGBP, 'GBP', 2) + '</td>' +
'<td class="num">' + formatAmount(row.RealisedGainGBP, 'GBP', 2) + '</td>' +
'<td class="num">' + row.IsFinalPosition.toString() + '</td>' +
'<td>' + (row.Notes == null ? '' : row.Notes) + '</td>' +
'</tr>'
}
//Fetch the ledger data
let ledger = {};
try {
ledger = await $.ajax({
type: "POST",
contentType: "application/json",
url: "SharePrices.aspx/GetTradeHistory"
});
}
catch (error) {
console.error({ MSG: "Error getting trade history", Error: error });
}
createLedgerTable();
//Create the ledger table rows
for (let rowNo = 0; rowNo < ledger.length; rowNo++) {
let r = ledger[rowNo];
let newContent = getContentRow(r);
$("#ledgerTable tbody").append(newContent);
}
//Add the tableSorter filter widget
let $table = $("#ledgerTable").tablesorter({
//theme: 'blue',
// this is the default setting
//cssChildRow: "tablesorter-childRow",
// initialize zebra and filter widgets
widgets: ["zebra", "filter", "stickyHeaders"],
widgetOptions: {
// output default: '{page}/{totalPages}'
// possible variables: {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows}
//pager_output: '{startRow} - {endRow} / {filteredRows} ({totalRows})',
//pager_removeRows: false,
// include child row content while filtering, if true
//filter_childRows: true,
// class name applied to filter row and each input
/*filter_cssFilter: 'tablesorter-filter',
filter_filteredRow: 'filtered',
// search from beginning
filter_startsWith: false,
// Set this option to false to make the searches case sensitive
filter_ignoreCase: true,
*/
stickyHeaders_attachTo: '#ledgerDiv',
//stickyHeaders_zIndex: 2,
zebra: [ 'odd', 'even']
}
});
}