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

@ -25,6 +25,22 @@ Public Class DataAccessLayer
DisposeSQLCommand(c)
End Function
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)
@ -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

@ -117,6 +117,16 @@ Public Class SharePrices
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
End Sub
<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,20 +370,21 @@ 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")
'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
@ -390,6 +406,31 @@ 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

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) {
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']
}
});
}