Added Ledger, started removing Holdings
This commit is contained in:
parent
e4f6c3d707
commit
5516aa1362
1026
Websites/SharePrices/.vs/SharePrices/config/applicationhost.config
Normal file
1026
Websites/SharePrices/.vs/SharePrices/config/applicationhost.config
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -1,12 +1,138 @@
|
|||||||
{
|
{
|
||||||
"Version": 1,
|
"Version": 1,
|
||||||
"WorkspaceRootPath": "\\\\OZHOST1\\d$\\Documents\\Visual Studio Projects\\SharePrices\\",
|
"WorkspaceRootPath": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\",
|
||||||
"Documents": [],
|
"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": [
|
"DocumentGroupContainers": [
|
||||||
{
|
{
|
||||||
"Orientation": 0,
|
"Orientation": 0,
|
||||||
"VerticalTabListWidth": 256,
|
"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": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -1,12 +1,135 @@
|
|||||||
{
|
{
|
||||||
"Version": 1,
|
"Version": 1,
|
||||||
"WorkspaceRootPath": "C:\\Users\\Steve.OZDOMAIN\\source\\repos\\Steves_Code\\Websites\\SharePrices\\",
|
"WorkspaceRootPath": "C:\\Users\\TSUser\\sources\\repos\\Steves_Code\\Websites\\SharePrices\\",
|
||||||
"Documents": [],
|
"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": [
|
"DocumentGroupContainers": [
|
||||||
{
|
{
|
||||||
"Orientation": 0,
|
"Orientation": 0,
|
||||||
"VerticalTabListWidth": 256,
|
"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": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
Binary file not shown.
@ -12,20 +12,36 @@ Public Class DataAccessLayer
|
|||||||
Public Volume As Int64
|
Public Volume As Int64
|
||||||
End Class
|
End Class
|
||||||
|
|
||||||
Public Shared Function GetInstruments() As String
|
Public Shared Function GetInstruments() As String
|
||||||
Dim c As SqlCommand = GetSQLCommand("usp_GetInstruments")
|
Dim c As SqlCommand = GetSQLCommand("usp_GetInstruments")
|
||||||
Dim Instruments As String = DataReaderToJSONString(c.ExecuteReader())
|
Dim Instruments As String = DataReaderToJSONString(c.ExecuteReader())
|
||||||
c = GetSQLCommand("usp_GetHoldings")
|
c = GetSQLCommand("usp_GetHoldings")
|
||||||
Dim Holdings As String = DataReaderToJSONString(c.ExecuteReader())
|
Dim Holdings As String = DataReaderToJSONString(c.ExecuteReader())
|
||||||
c = GetSQLCommand("usp_GetHoldingsHistory")
|
c = GetSQLCommand("usp_GetHoldingsHistory")
|
||||||
Dim HoldingsHistory As String = DataReaderToJSONString(c.ExecuteReader())
|
Dim HoldingsHistory As String = DataReaderToJSONString(c.ExecuteReader())
|
||||||
c = GetSQLCommand("usp_GetAccounts")
|
c = GetSQLCommand("usp_GetAccounts")
|
||||||
Dim Accounts As String = DataReaderToJSONString(c.ExecuteReader())
|
Dim Accounts As String = DataReaderToJSONString(c.ExecuteReader())
|
||||||
GetInstruments = "{""Instruments"": " + Instruments + ", ""Holdings"": " + Holdings + ", ""HoldingsHistory"": " + HoldingsHistory + ", ""Accounts"": " + Accounts + "}"
|
GetInstruments = "{""Instruments"": " + Instruments + ", ""Holdings"": " + Holdings + ", ""HoldingsHistory"": " + HoldingsHistory + ", ""Accounts"": " + Accounts + "}"
|
||||||
DisposeSQLCommand(c)
|
DisposeSQLCommand(c)
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Public Shared Function AddInstrument(Symbol As String, FullName As String) As String
|
Public Shared Function GetAggregatedCurrentHoldings() As String
|
||||||
|
Dim c As SqlCommand = GetSQLCommand("usp_GetAggregatedCurrentHoldings")
|
||||||
|
GetAggregatedCurrentHoldings = DataReaderToJSONString(c.ExecuteReader())
|
||||||
|
DisposeSQLCommand(c)
|
||||||
|
End Function
|
||||||
|
Public Shared Function GetCurrentHoldings() As String
|
||||||
|
Dim c As SqlCommand = GetSQLCommand("usp_GetCurrentHoldings")
|
||||||
|
GetCurrentHoldings = DataReaderToJSONString(c.ExecuteReader())
|
||||||
|
DisposeSQLCommand(c)
|
||||||
|
End Function
|
||||||
|
Public Shared Function GetTradeHistory() As String
|
||||||
|
Dim c As SqlCommand = GetSQLCommand("usp_GetTradeHistory")
|
||||||
|
GetTradeHistory = DataReaderToJSONString(c.ExecuteReader())
|
||||||
|
DisposeSQLCommand(c)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Shared Function AddInstrument(Symbol As String, FullName As String) As String
|
||||||
Dim c As SqlCommand = GetSQLCommand("usp_InsertInstrument")
|
Dim c As SqlCommand = GetSQLCommand("usp_InsertInstrument")
|
||||||
c.Parameters.AddWithValue("Symbol", Symbol)
|
c.Parameters.AddWithValue("Symbol", Symbol)
|
||||||
c.Parameters.AddWithValue("FullName", FullName)
|
c.Parameters.AddWithValue("FullName", FullName)
|
||||||
@ -258,6 +274,7 @@ Public Class DataAccessLayer
|
|||||||
Select Case dr.GetDataTypeName(fieldNo).ToUpper
|
Select Case dr.GetDataTypeName(fieldNo).ToUpper
|
||||||
Case "CHAR", "VARCHAR" : thisRow += """" + dr(fieldNo) + """"
|
Case "CHAR", "VARCHAR" : thisRow += """" + dr(fieldNo) + """"
|
||||||
Case "DATETIME", "SMALLDATETIME", "DATETIME2", "DATE" : thisRow += ToEpochTime(dr(fieldNo)).ToString()
|
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()
|
Case Else : thisRow += dr(fieldNo).ToString()
|
||||||
End Select
|
End Select
|
||||||
End If
|
End If
|
||||||
|
@ -40,7 +40,8 @@
|
|||||||
<a id="navCharts" class="activenav" onclick="showTab(this)" data-div="chartDiv">Charts</a>
|
<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="navCurrentHoldings" onclick="showTab(this);" data-div="holdingsDiv">Holdings</a>
|
||||||
<a id="navAccounts" onclick="showTab(this);" data-div="accountsDiv">Accounts</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="navHist" onclick="showTab(this); importedFunctions.refreshHistoryChart();" data-div="histDiv">History</a>
|
||||||
<a id="navLog" onclick="showTab(this);" data-div="logDiv">Log</a>
|
<a id="navLog" onclick="showTab(this);" data-div="logDiv">Log</a>
|
||||||
</td>
|
</td>
|
||||||
@ -73,12 +74,26 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="tabParent">
|
<div class="tabParent">
|
||||||
<div class="tabContainer" id="holdingsDiv">
|
<div class="tabContainer" id="holdingsDiv">
|
||||||
|
<!-- Old holdings table
|
||||||
<div>
|
<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>
|
||||||
<div id="holdingsTableDiv">Holdings table</div>
|
<div id="holdingsTableDiv">Holdings table</div>
|
||||||
<br />
|
<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>
|
<div id="watchlistTableDiv">Watchlist table</div>
|
||||||
|
<!-- Old pie charts
|
||||||
<br />
|
<br />
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
@ -91,6 +106,7 @@
|
|||||||
<td>Current Value per Account<div id="divAccountValue" class="HoldingCurrenciesChart"></div></td>
|
<td>Current Value per Account<div id="divAccountValue" class="HoldingCurrenciesChart"></div></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
!-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tabParent">
|
<div class="tabParent">
|
||||||
@ -101,9 +117,14 @@
|
|||||||
<div id="accountsSummaryDiv"></div>
|
<div id="accountsSummaryDiv"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tabParent">
|
||||||
|
<div class="tabContainer" id="ledgerDiv">Analysis</div>
|
||||||
|
</div>
|
||||||
|
<!--
|
||||||
<div class="tabParent">
|
<div class="tabParent">
|
||||||
<div class="tabContainer" id="analDiv">Analysis</div>
|
<div class="tabContainer" id="analDiv">Analysis</div>
|
||||||
</div>
|
</div>
|
||||||
|
!-->
|
||||||
<div class="tabParent">
|
<div class="tabParent">
|
||||||
<div class="tabContainer" id="histDiv" style="height: 85%;">History Chart</div>
|
<div class="tabContainer" id="histDiv" style="height: 85%;">History Chart</div>
|
||||||
<!--<span onclick="refreshHistoryChart();">Refresh Chart</span>!-->
|
<!--<span onclick="refreshHistoryChart();">Refresh Chart</span>!-->
|
||||||
@ -141,6 +162,7 @@
|
|||||||
<div id="chartTooltip" class="chart-tooltip"></div>
|
<div id="chartTooltip" class="chart-tooltip"></div>
|
||||||
<script>
|
<script>
|
||||||
let importedFunctions = {};
|
let importedFunctions = {};
|
||||||
|
|
||||||
function logInfo(msg) {
|
function logInfo(msg) {
|
||||||
console.info(msg);
|
console.info(msg);
|
||||||
if (typeof msg == 'object') {
|
if (typeof msg == 'object') {
|
||||||
@ -202,6 +224,10 @@
|
|||||||
break;
|
break;
|
||||||
case 'hideEmptyAccounts':
|
case 'hideEmptyAccounts':
|
||||||
break;
|
break;
|
||||||
|
case 'showSubtotals':
|
||||||
|
break;
|
||||||
|
case 'showCryptoHoldings':
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -209,7 +235,6 @@
|
|||||||
import {
|
import {
|
||||||
addInstrument,
|
addInstrument,
|
||||||
createSharesTable,
|
createSharesTable,
|
||||||
createSharesTableRow,
|
|
||||||
getInstruments,
|
getInstruments,
|
||||||
redrawAllSharesCharts,
|
redrawAllSharesCharts,
|
||||||
refreshHistoryChart,
|
refreshHistoryChart,
|
||||||
@ -220,10 +245,12 @@
|
|||||||
showModalDialog_FullScreenChart,
|
showModalDialog_FullScreenChart,
|
||||||
showModalDialog_SoldHolding
|
showModalDialog_SoldHolding
|
||||||
} from "./scripts/SharePrices.js";
|
} from "./scripts/SharePrices.js";
|
||||||
|
|
||||||
|
import { updateLedgerTable } from "./scripts/SharePrices_Ledger.js";
|
||||||
|
|
||||||
importedFunctions = {
|
importedFunctions = {
|
||||||
addInstrument: addInstrument,
|
addInstrument: addInstrument,
|
||||||
createSharesTable: createSharesTable,
|
createSharesTable: createSharesTable,
|
||||||
createSharesTableRow: createSharesTableRow,
|
|
||||||
getInstruments: getInstruments,
|
getInstruments: getInstruments,
|
||||||
redrawAllSharesCharts: redrawAllSharesCharts,
|
redrawAllSharesCharts: redrawAllSharesCharts,
|
||||||
refreshHistoryChart: refreshHistoryChart,
|
refreshHistoryChart: refreshHistoryChart,
|
||||||
@ -232,7 +259,8 @@
|
|||||||
showModalDialog_AddInstrument: showModalDialog_AddInstrument,
|
showModalDialog_AddInstrument: showModalDialog_AddInstrument,
|
||||||
showModalDialog_AddHolding: showModalDialog_AddHolding,
|
showModalDialog_AddHolding: showModalDialog_AddHolding,
|
||||||
showModalDialog_FullScreenChart: showModalDialog_FullScreenChart,
|
showModalDialog_FullScreenChart: showModalDialog_FullScreenChart,
|
||||||
showModalDialog_SoldHolding: showModalDialog_SoldHolding
|
showModalDialog_SoldHolding: showModalDialog_SoldHolding,
|
||||||
|
updateLedgerTable: updateLedgerTable
|
||||||
}
|
}
|
||||||
|
|
||||||
function initPage() {
|
function initPage() {
|
||||||
@ -258,7 +286,13 @@
|
|||||||
$("#groupBySymbol").prop('checked', (c == 'true') ? true : false);
|
$("#groupBySymbol").prop('checked', (c == 'true') ? true : false);
|
||||||
|
|
||||||
c = Cookies.get('hideEmptyAccounts');
|
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();
|
getInstruments();
|
||||||
}
|
}
|
||||||
|
@ -105,19 +105,29 @@ Public Class SharePrices
|
|||||||
'Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.SubmitDailyData(" + Symbol + ") completed. Duration = " + DateDiff(DateInterval.Second, startDT, endDT).ToString())
|
'Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.SubmitDailyData(" + Symbol + ") completed. Duration = " + DateDiff(DateInterval.Second, startDT, endDT).ToString())
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
<WebMethod()>
|
<WebMethod()>
|
||||||
Public Shared Sub SubmitIntradayData(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData), GMTOffset As Integer, Currency As String, CurrentPrice As Double, InstrumentType As String, TradeDayStart As Long, TradeDayEnd As Long)
|
Public Shared Sub SubmitIntradayData(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData), GMTOffset As Integer, Currency As String, CurrentPrice As Double, InstrumentType As String, TradeDayStart As Long, TradeDayEnd As Long)
|
||||||
'Public Shared Sub SubmitIntradayData(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData))
|
'Public Shared Sub SubmitIntradayData(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData))
|
||||||
Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.SubmitIntradayData webmethod: " + Symbol)
|
Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.SubmitIntradayData webmethod: " + Symbol)
|
||||||
|
|
||||||
DataAccessLayer.UpdateInstrument(Symbol, GMTOffset, Currency, CurrentPrice, InstrumentType, FromEpochTime(TradeDayStart), FromEpochTime(TradeDayEnd))
|
DataAccessLayer.UpdateInstrument(Symbol, GMTOffset, Currency, CurrentPrice, InstrumentType, FromEpochTime(TradeDayStart), FromEpochTime(TradeDayEnd))
|
||||||
|
|
||||||
Dim responseText As String = DataAccessLayer.InsertIntradayData(Symbol, DailyPrices)
|
Dim responseText As String = DataAccessLayer.InsertIntradayData(Symbol, DailyPrices)
|
||||||
If responseText = "" Then responseText = "[]"
|
If responseText = "" Then responseText = "[]"
|
||||||
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
|
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
<WebMethod()>
|
<WebMethod()>
|
||||||
|
Public Shared Sub UpdateInstrument(Symbol As String, GMTOffset As Integer, Currency As String, CurrentPrice As Double, InstrumentType As String, TradeDayStart As Long, TradeDayEnd As Long)
|
||||||
|
Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.UpdateInstrument webmethod: " + Symbol)
|
||||||
|
|
||||||
|
DataAccessLayer.UpdateInstrument(Symbol, GMTOffset, Currency, CurrentPrice, InstrumentType, FromEpochTime(TradeDayStart), FromEpochTime(TradeDayEnd))
|
||||||
|
|
||||||
|
Dim responseText As String = "{ ""Result"": ""Success"" }"
|
||||||
|
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
<WebMethod()>
|
||||||
Public Shared Sub SwapInstrumentDisplayOrders(Symbol1 As String, Symbol2 As String)
|
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)
|
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)
|
'webClient.Proxy = New System.Net.WebProxy("127.0.0.1", 8888)
|
||||||
|
|
||||||
'Set headers required by Yahoo Finance
|
'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", "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-Language", "en-GB,en;q=0.5")
|
||||||
'webClient.Headers.Add("Accept-Encoding", "gzip, deflate, br")
|
'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-Site", "none()")
|
||||||
webClient.Headers.Add("Sec-Fetch-User", "?1")
|
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)
|
DownloadYahooFinanceWebString = webClient.DownloadString(queryString)
|
||||||
End Function
|
End Function
|
||||||
<WebMethod()>
|
<WebMethod()>
|
||||||
@ -355,21 +370,22 @@ Public Class SharePrices
|
|||||||
Public Shared Sub SearchYahooFinanceShares(SearchString As String)
|
Public Shared Sub SearchYahooFinanceShares(SearchString As String)
|
||||||
Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.SearchYahooFinanceShares webmethod: " + SearchString)
|
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("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", "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-Language", "en-GB,en;q=0.5")
|
||||||
'webClient.Headers.Add("Accept-Encoding", "gzip, deflate, br")
|
''webClient.Headers.Add("Accept-Encoding", "gzip, deflate, br")
|
||||||
'Connection: keep -alive
|
''Connection: keep -alive
|
||||||
webClient.Headers.Add("Upgrade-Insecure-Requests", "1")
|
'webClient.Headers.Add("Upgrade-Insecure-Requests", "1")
|
||||||
webClient.Headers.Add("Sec-Fetch-Dest", "document()")
|
'webClient.Headers.Add("Sec-Fetch-Dest", "document()")
|
||||||
webClient.Headers.Add("Sec-Fetch-Mode", "navigate()")
|
'webClient.Headers.Add("Sec-Fetch-Mode", "navigate()")
|
||||||
webClient.Headers.Add("Sec-Fetch-Site", "none()")
|
'webClient.Headers.Add("Sec-Fetch-Site", "none()")
|
||||||
webClient.Headers.Add("Sec-Fetch-User", "?1")
|
'webClient.Headers.Add("Sec-Fetch-User", "?1")
|
||||||
|
|
||||||
Dim responseText As String = webClient.DownloadString("https://query1.finance.yahoo.com/v1/finance/search?q=" + SearchString + ""esCount=6&newsCount=0&enableFuzzyQuery=false"esQueryId=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 + ""esCount=6&newsCount=0&enableFuzzyQuery=false"esQueryId=tss_match_phrase_query&multiQuoteQueryId=multi_quote_single_token_query&newsQueryId=news_ss_symbols&enableCb=false&enableNavLinks=false&vespaNewsTimeoutMs=600")
|
||||||
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
|
Dim responseText As String = DownloadYahooFinanceWebString("https://query1.finance.yahoo.com/v1/finance/search?q=" + SearchString + ""esCount=6&newsCount=0&enableFuzzyQuery=false"esQueryId=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
|
End Sub
|
||||||
|
|
||||||
<WebMethod()>
|
<WebMethod()>
|
||||||
@ -390,11 +406,36 @@ Public Class SharePrices
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<WebMethod()>
|
||||||
|
Public Shared Sub GetAggregatedCurrentHoldings()
|
||||||
|
'Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.GetAggregatedCurrentHoldings webmethod")
|
||||||
|
|
||||||
|
Dim responseText As String = DataAccessLayer.GetAggregatedCurrentHoldings()
|
||||||
|
If responseText = "" Then responseText = "[]"
|
||||||
|
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
|
||||||
|
End Sub
|
||||||
|
<WebMethod()>
|
||||||
|
Public Shared Sub GetCurrentHoldings()
|
||||||
|
'Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.GetCurrentHoldings webmethod")
|
||||||
|
|
||||||
|
Dim responseText As String = DataAccessLayer.GetCurrentHoldings()
|
||||||
|
If responseText = "" Then responseText = "[]"
|
||||||
|
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
|
||||||
|
End Sub
|
||||||
|
<WebMethod()>
|
||||||
|
Public Shared Sub GetTradeHistory()
|
||||||
|
'Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.GetTradeHistory webmethod")
|
||||||
|
|
||||||
|
Dim responseText As String = DataAccessLayer.GetTradeHistory()
|
||||||
|
If responseText = "" Then responseText = "[]"
|
||||||
|
SetResponseAndCompleteRequest(HttpContext.Current, "application/json", responseText)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<WebMethod()>
|
|
||||||
|
<WebMethod()>
|
||||||
Public Shared Sub TestWebMethod(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData))
|
Public Shared Sub TestWebMethod(Symbol As String, DailyPrices As List(Of DataAccessLayer.PriceData))
|
||||||
Dim startDT As Date = Now()
|
Dim startDT As Date = Now()
|
||||||
Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.TestWebMethod: " + Symbol)
|
Debug.Print(Now().ToString("yyyy-MM-dd HH:mm:ss") + " - SharePrices.TestWebMethod: " + Symbol)
|
||||||
|
@ -260,8 +260,10 @@
|
|||||||
<Content Include="scripts\jquery\jquery.marquee.min.js" />
|
<Content Include="scripts\jquery\jquery.marquee.min.js" />
|
||||||
<Content Include="scripts\js.cookie-2.2.1.min.js" />
|
<Content Include="scripts\js.cookie-2.2.1.min.js" />
|
||||||
<Content Include="scripts\SharePrices.js" />
|
<Content Include="scripts\SharePrices.js" />
|
||||||
|
<Content Include="scripts\SharePrices_Accounts.js" />
|
||||||
<Content Include="scripts\SharePrices_Charts.js" />
|
<Content Include="scripts\SharePrices_Charts.js" />
|
||||||
<Content Include="scripts\SharePrices_Holdings.js" />
|
<Content Include="scripts\SharePrices_Holdings.js" />
|
||||||
|
<Content Include="scripts\SharePrices_Ledger.js" />
|
||||||
<Content Include="scripts\tablesorter\dragtable.mod.css" />
|
<Content Include="scripts\tablesorter\dragtable.mod.css" />
|
||||||
<Content Include="scripts\tablesorter\filter.formatter.css" />
|
<Content Include="scripts\tablesorter\filter.formatter.css" />
|
||||||
<Content Include="scripts\tablesorter\highlights.css" />
|
<Content Include="scripts\tablesorter\highlights.css" />
|
||||||
|
@ -39,6 +39,11 @@ td {vertical-align: top;}
|
|||||||
#spnCurrencies a { color: white; }
|
#spnCurrencies a { color: white; }
|
||||||
#spnCurrencies a:visited { 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:link { color: #f473ff; }
|
||||||
a:visited { color: #9a34b7; }
|
a:visited { color: #9a34b7; }
|
||||||
|
|
||||||
@ -47,20 +52,49 @@ a:visited { color: #9a34b7; }
|
|||||||
.miniHoldings th { font-size: x-small; border: 1px solid #505050; }
|
.miniHoldings th { font-size: x-small; border: 1px solid #505050; }
|
||||||
.soldHolding { text-decoration: line-through; }
|
.soldHolding { text-decoration: line-through; }
|
||||||
|
|
||||||
/*tr.highlighted, td.highlighted { background-color: gold; }*/
|
|
||||||
.highlighted { background-color: gold; }
|
|
||||||
|
|
||||||
.mainHoldings { font-size: small; }
|
.mainHoldings { font-size: small; }
|
||||||
table.mainHoldings { width: 100%; }
|
table.mainHoldings { width: 100%; }
|
||||||
|
table.mainHoldings th { background-color: #101040; }
|
||||||
table.mainHoldings th, table.mainHoldings td { padding-left: 5px; padding-right: 5px; }
|
table.mainHoldings th, table.mainHoldings td { padding-left: 5px; padding-right: 5px; }
|
||||||
table.mainHoldings tr:hover { background-color: #404090; }
|
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 { width: 100%; }*/
|
||||||
table.watchlist th, table.watchlist td { padding-left: 5px; padding-right: 5px; }
|
table.watchlist th, table.watchlist td { padding-left: 5px; padding-right: 5px; }
|
||||||
table.watchlist tr:hover { background-color: #404090; }
|
table.watchlist tr:hover { background-color: #404090; }
|
||||||
|
|
||||||
.accounts { font-size: small; }
|
.accounts { font-size: 9pt; }
|
||||||
/*table.accounts { width: 100%; }*/
|
/*table.accounts { width: 100%; }*/
|
||||||
table.accounts th, table.accounts td { padding-left: 5px; padding-right: 5px; }
|
table.accounts th, table.accounts td { padding-left: 5px; padding-right: 5px; }
|
||||||
table.accounts tr:hover { background-color: #404090; }
|
table.accounts tr:hover { background-color: #404090; }
|
||||||
@ -137,8 +171,6 @@ td.no-border { border: 0px; text-align: right; }
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.deletebutton { color: red; cursor: pointer; }
|
.deletebutton { color: red; cursor: pointer; }
|
||||||
|
|
||||||
span.addHolding { 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 { width: 600px; overflow: hidden; font-size: small; /*border: 1px solid #ccc;*/ /*background: #ccc;*/ }
|
||||||
.marquee div { padding: 1px 0px 1px 0px; }
|
.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%; }
|
|
||||||
|
|
||||||
*/
|
|
@ -1,12 +1,12 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
//Testing gitea.copeland-bowen.com #3
|
|
||||||
|
|
||||||
import {formatAmount, generateAxisBreaks, getProfitOrLoss, timespans} from "./SharePrices_Common.js";
|
import {formatAmount, generateAxisBreaks, getProfitOrLoss, timespans} from "./SharePrices_Common.js";
|
||||||
import {createLongChart, createMidChart, createShortChart, createSingleDayChart, createChart_Flot,
|
import {createLongChart, createMidChart, createShortChart, createSingleDayChart, createChart_Flot,
|
||||||
createChart, createFullScreenChart, createFullScreenChart_Flot, createFullScreenComparisonChart,
|
createChart, createFullScreenChart, createFullScreenChart_Flot, createFullScreenComparisonChart,
|
||||||
createFullScreenComparisonChart_Flot
|
createFullScreenComparisonChart_Flot
|
||||||
} from "./SharePrices_Charts.js";
|
} from "./SharePrices_Charts.js";
|
||||||
import {createHoldingsTable} from "./SharePrices_Holdings.js";
|
import { updateHoldingsTables } from "./SharePrices_Holdings.js";
|
||||||
|
import { createAccountsTables } from "./SharePrices_Accounts.js";
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
@ -26,14 +26,43 @@ var vars = {
|
|||||||
previousDay: 0,
|
previousDay: 0,
|
||||||
previousClose: 0,
|
previousClose: 0,
|
||||||
previousWeek: 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 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 lastSuccessfulFetch;
|
||||||
var currencyFormatter = new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' });
|
var currencyFormatter = new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' });
|
||||||
var instrumentSearchResults = [];
|
var instrumentSearchResults = [];
|
||||||
var tablesUpdateTimings = { lastUpdate: 0, timeBetweenUpdates: 10000, timer: 0, updateNeeded: true };
|
var tablesUpdateTimings = { lastUpdate: 0, timeBetweenUpdates: 15000, timer: 0, updateNeeded: true };
|
||||||
var indexMarquee = {
|
var indexMarquee = {
|
||||||
newContent: '',
|
newContent: '',
|
||||||
currentContent: '',
|
currentContent: '',
|
||||||
@ -127,7 +156,7 @@ var Instruments = {
|
|||||||
let lastFetchedDate = this.Data[0].lastFetchedIntraday;
|
let lastFetchedDate = this.Data[0].lastFetchedIntraday;
|
||||||
let lastFetchedIndex = 0;
|
let lastFetchedIndex = 0;
|
||||||
for (let i = 1; i < this.Data.length; i++) {
|
for (let i = 1; i < this.Data.length; i++) {
|
||||||
if (this.Data[i].Symbol != 'GBP' && this.Data[i].Symbol != 'GBp') {
|
if (this.Data[i].Symbol != 'GBP' && this.Data[i].Symbol != 'GBp' && 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 = this.Data[i].MaxIntradayDate };
|
||||||
if (!this.Data[i].lastFetchedIntraday) { this.Data[i].lastFetchedIntraday = new Date().getTime() - vars.fetchTiming.fetchIntervalIntraday + (2 * timespans.oneMinute) };
|
if (!this.Data[i].lastFetchedIntraday) { this.Data[i].lastFetchedIntraday = new Date().getTime() - vars.fetchTiming.fetchIntervalIntraday + (2 * timespans.oneMinute) };
|
||||||
if (lastFetchedDate > this.Data[i].lastFetchedIntraday) {
|
if (lastFetchedDate > this.Data[i].lastFetchedIntraday) {
|
||||||
@ -247,6 +276,14 @@ Date.prototype.yyyymmddhhmmss = function () {
|
|||||||
var ss = this.getSeconds().toString();
|
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]);
|
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 () {
|
Date.prototype.yyyymmdd = function () {
|
||||||
var yyyy = this.getFullYear().toString();
|
var yyyy = this.getFullYear().toString();
|
||||||
var mm = (this.getMonth() + 1).toString(); // getMonth() is zero-based
|
var mm = (this.getMonth() + 1).toString(); // getMonth() is zero-based
|
||||||
@ -289,7 +326,11 @@ Number.prototype.autoScale = function () {
|
|||||||
return this.toFixed(1);
|
return this.toFixed(1);
|
||||||
} else {
|
} else {
|
||||||
if (a < 0.001) {
|
if (a < 0.001) {
|
||||||
return this.toExponential(2);
|
if (a == 0) {
|
||||||
|
return '0';
|
||||||
|
} else {
|
||||||
|
return this.toExponential(2);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return this.toFixed(2);
|
return this.toFixed(2);
|
||||||
}
|
}
|
||||||
@ -383,7 +424,7 @@ export function createSharesTable() {
|
|||||||
logError({ MSG: "Error in createSharesTable", err: err });
|
logError({ MSG: "Error in createSharesTable", err: err });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export function createSharesTableRow(instrument) {
|
function createSharesTableRow(instrument) {
|
||||||
function getHoldingsTable(Instrument) {
|
function getHoldingsTable(Instrument) {
|
||||||
if (Instrument.Holdings.length > 0) {
|
if (Instrument.Holdings.length > 0) {
|
||||||
let totalUnits = 0;
|
let totalUnits = 0;
|
||||||
@ -769,7 +810,7 @@ function getYahooDailyData(Instrument) {
|
|||||||
createLongChart(Instrument);
|
createLongChart(Instrument);
|
||||||
} else {
|
} else {
|
||||||
//console.info("No data received for symbol: " + Instrument.Symbol);
|
//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) {
|
failure: function (response) {
|
||||||
@ -829,14 +870,14 @@ function getYahooIntradayData(Instrument) {
|
|||||||
createMidChart(Instrument);
|
createMidChart(Instrument);
|
||||||
createShortChart(Instrument);
|
createShortChart(Instrument);
|
||||||
} else {
|
} else {
|
||||||
console.info("No NEW data received for symbol: " + Instrument.Symbol);
|
console.info("getYahooIntradayData - No NEW data received for symbol: " + Instrument.Symbol);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//logWarning({ MSG: "No timestamp series received for symbol: " + Instrument.Symbol, response: response });
|
//logWarning({ MSG: "No timestamp series received for symbol: " + Instrument.Symbol, response: response });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//console.warn("No data received for symbol: " + Instrument.Symbol);
|
//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) {
|
failure: function (response) {
|
||||||
@ -859,10 +900,15 @@ function getYahooSingleDayData(Instrument) {
|
|||||||
success: function (response) {
|
success: function (response) {
|
||||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
||||||
let newData = [];
|
let newData = [];
|
||||||
|
let dbUpdateNeeded = false;
|
||||||
|
|
||||||
if (response.chart && response.chart.result) {
|
if (response.chart && response.chart.result) {
|
||||||
let dataSeries = response.chart.result[0];
|
let dataSeries = response.chart.result[0];
|
||||||
|
|
||||||
if (dataSeries.meta) {
|
if (dataSeries.meta) {
|
||||||
|
if (Instrument.CurrentPrice != dataSeries.meta.regularMarketPrice) {
|
||||||
|
dbUpdateNeeded = true;
|
||||||
|
}
|
||||||
Instrument.SingleDayPreviousClose = dataSeries.meta.previousClose;
|
Instrument.SingleDayPreviousClose = dataSeries.meta.previousClose;
|
||||||
Instrument.SingleDayStartDate = dataSeries.meta.currentTradingPeriod.regular.start * 1000;
|
Instrument.SingleDayStartDate = dataSeries.meta.currentTradingPeriod.regular.start * 1000;
|
||||||
Instrument.SingleDayEndDate = dataSeries.meta.currentTradingPeriod.regular.end * 1000;
|
Instrument.SingleDayEndDate = dataSeries.meta.currentTradingPeriod.regular.end * 1000;
|
||||||
@ -908,12 +954,26 @@ function getYahooSingleDayData(Instrument) {
|
|||||||
} else {
|
} else {
|
||||||
//console.warn("No timestamp series received for symbol: " + Instrument.Symbol);
|
//console.warn("No timestamp series received for symbol: " + Instrument.Symbol);
|
||||||
//logWarning("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 {
|
} else {
|
||||||
//console.info("No data received for symbol: " + Instrument.Symbol);
|
//console.info("No data received for symbol: " + Instrument.Symbol);
|
||||||
//logInfo("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) {
|
failure: function (response) {
|
||||||
@ -1014,6 +1074,34 @@ function submitNewDailyData(Instrument, NewData) {
|
|||||||
}
|
}
|
||||||
function submitNewIntradayData(Instrument, NewData) {
|
function submitNewIntradayData(Instrument, NewData) {
|
||||||
//console.info("submitNewIntradayData");
|
//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({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
@ -1026,6 +1114,7 @@ function submitNewIntradayData(Instrument, NewData) {
|
|||||||
},
|
},
|
||||||
failure: function (response) { console.error("SubmitIntradayData error: " + JSON.stringify(response)); }
|
failure: function (response) { console.error("SubmitIntradayData error: " + JSON.stringify(response)); }
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
//Add new data to Instrument
|
//Add new data to Instrument
|
||||||
let currentSummary = {};
|
let currentSummary = {};
|
||||||
@ -1050,207 +1139,20 @@ function submitNewIntradayData(Instrument, NewData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateTables() {
|
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()) {
|
if (tablesUpdateTimings.updateNeeded && tablesUpdateTimings.lastUpdate + tablesUpdateTimings.timeBetweenUpdates < new Date().getTime()) {
|
||||||
tablesUpdateTimings.lastUpdate = new Date().getTime();
|
tablesUpdateTimings.lastUpdate = new Date().getTime();
|
||||||
|
|
||||||
createHoldingsTable(Instruments, vars);
|
//createHoldingsTable(Instruments, vars);
|
||||||
createAccountsTables();
|
createAccountsTables(Instruments);
|
||||||
createAnalysisTable();
|
createAnalysisTable();
|
||||||
createIndexMarquee();
|
createIndexMarquee();
|
||||||
updateCurrenciesSpan();
|
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"> </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"> </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"> </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 createAnalysisTable() {
|
||||||
function getHighestPriceBeforeDate(Instrument, maxDT) {
|
function getHighestPriceBeforeDate(Instrument, maxDT) {
|
||||||
if (Instrument.DailyData) {
|
if (Instrument.DailyData) {
|
||||||
@ -1465,15 +1367,16 @@ function createIndexMarquee() {
|
|||||||
//if (i.InstrumentType == 'INDEX' || i.InstrumentType == 'FUTURE') {
|
//if (i.InstrumentType == 'INDEX' || i.InstrumentType == 'FUTURE') {
|
||||||
if (i.ShowInMarquee == 'A') {
|
if (i.ShowInMarquee == 'A') {
|
||||||
let openOrClosed = Instruments.MarketIsOpen(i) == 1 ? 'open' : 'closed';
|
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) {
|
if (i.SingleDayPreviousClose) {
|
||||||
let percentChange = ((i.CurrentPrice / i.SingleDayPreviousClose) - 1) * 100;
|
let percentChange = ((i.CurrentPrice / i.SingleDayPreviousClose) - 1) * 100;
|
||||||
if (percentChange < 0) {
|
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 {
|
} 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 {
|
} else {
|
||||||
t.push('<font class="' + openOrClosed + '">' + i.DisplayName + '<br/>' + i.CurrentPrice + '</font>');
|
t.push('<font class="' + openOrClosed + '">' + label + '<br/>' + i.CurrentPrice + '</font>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
384
Websites/SharePrices/SharePrices/scripts/SharePrices_Accounts.js
Normal file
384
Websites/SharePrices/SharePrices/scripts/SharePrices_Accounts.js
Normal 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"> </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"> </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"> </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"> </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"> </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"> </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);
|
||||||
|
}
|
@ -84,4 +84,13 @@ export function generateAxisBreaks(data, maxGapSize, breakSize) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return breaks;
|
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;"> </div>' +
|
||||||
|
'<div class="pcLabel" style="width:' + cellWidth + 'px">' + labelText + '</div>' +
|
||||||
|
'</td>';
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { formatAmount } from "./SharePrices_Common.js";
|
import { formatAmount, getPercentCell } from "./SharePrices_Common.js";
|
||||||
|
/*
|
||||||
export function createHoldingsTable(Instruments, vars) {
|
export function createHoldingsTable(Instruments, vars) {
|
||||||
function getHoldings() {
|
function getHoldings() {
|
||||||
function aggregateHoldings(i) {
|
function aggregateHoldings(i) {
|
||||||
@ -302,12 +302,17 @@ export function createHoldingsTable(Instruments, vars) {
|
|||||||
$('#holdingsTableDiv').html(tbl);
|
$('#holdingsTableDiv').html(tbl);
|
||||||
|
|
||||||
$("#tblMainHoldings").tablesorter({ ignoreCase: false });
|
$("#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) {
|
$('#tblMainHoldings').on('sortEnd', function (event) {
|
||||||
// Prints the current sort order to the console
|
// Prints the current sort order to the console
|
||||||
if (event.target.config.sortList.length > 3) {
|
if (event.target.config.sortList.length > 3) {
|
||||||
console.info({ "Setting sortHoldings Cookie (array too long?)": event.target.config.sortList });
|
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' });
|
Cookies.set('sortHoldings', event.target.config.sortList, { 'sameSite': 'strict' });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -354,14 +359,16 @@ export function createHoldingsTable(Instruments, vars) {
|
|||||||
//$("#" + rowID).addClass("highlighted");
|
//$("#" + rowID).addClass("highlighted");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function getPercentCell(backgroundColor, cellWidth, valuePercent, label) {
|
|
||||||
let labelText = label ? label : (valuePercent.toFixed(1) + '%');
|
//function getPercentCell(backgroundColor, cellWidth, valuePercent, label) {
|
||||||
let barWidth = ((valuePercent / 100) * cellWidth);
|
// let labelText = label ? label : (valuePercent.toFixed(1) + '%');
|
||||||
return '<td>' +
|
// let barWidth = ((valuePercent / 100) * cellWidth);
|
||||||
'<div class="pcBackground" style="background-color: ' + backgroundColor + '; width:' + barWidth.toFixed(0) + 'px;"> </div>' +
|
// return '<td>' +
|
||||||
'<div class="pcLabel" style="width:' + cellWidth + 'px">' + labelText + '</div>' +
|
// '<div class="pcBackground" style="background-color: ' + backgroundColor + '; width:' + barWidth.toFixed(0) + 'px;"> </div>' +
|
||||||
'</td>';
|
// '<div class="pcLabel" style="width:' + cellWidth + 'px">' + labelText + '</div>' +
|
||||||
}
|
// '</td>';
|
||||||
|
//}
|
||||||
|
|
||||||
function profitOrLoss(value) {
|
function profitOrLoss(value) {
|
||||||
return value >= 0 ? "profit" : "loss";
|
return value >= 0 ? "profit" : "loss";
|
||||||
}
|
}
|
||||||
@ -495,35 +502,6 @@ export function createHoldingsTable(Instruments, vars) {
|
|||||||
var resort = true;
|
var resort = true;
|
||||||
$("#tblMainHoldings").trigger("update", [resort]);
|
$("#tblMainHoldings").trigger("update", [resort]);
|
||||||
|
|
||||||
//Update the daily holdings value table in the DB
|
|
||||||
if (totalValueGBP != vars.lastTotalHoldingsValue) {
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
contentType: "application/json",
|
|
||||||
url: "SharePrices.aspx/SetTotalHoldingsValue",
|
|
||||||
dataType: "json",
|
|
||||||
data: JSON.stringify({ TotalValue: totalValueGBP }),
|
|
||||||
success: function (response) {
|
|
||||||
vars.previousDay = response.PreviousDay;
|
|
||||||
vars.previousClose = response.PreviousClose;
|
|
||||||
vars.previousWeek = response.PreviousWeekDate;
|
|
||||||
vars.previousWeekClose = response.PreviousWeekClose;
|
|
||||||
|
|
||||||
//Update the total holdings span in the site banner
|
|
||||||
let formattedAmount = formatAmount(totalValueGBP, "GBP", 0);
|
|
||||||
let holdingsHTML = '<span style="font-size: small;">Total holdings: </span>' + '<span style="font-size: large">' + formattedAmount + '</span> ';
|
|
||||||
holdingsHTML += '<span style="font-size: small;"><span class="' + (vars.previousClose < totalValueGBP ? 'profit">+' : 'loss">') + formatAmount(totalValueGBP - vars.previousClose, "GBP", 0) + '</span> (d)';
|
|
||||||
holdingsHTML += ' / <span class="' + (vars.previousWeekClose < totalValueGBP ? 'profit">+' : 'loss">') + formatAmount(totalValueGBP - vars.previousWeekClose, "GBP", 0) + '</span> (w)</span>';
|
|
||||||
$("#spnTotalHoldings").html(holdingsHTML);
|
|
||||||
//Set the page title
|
|
||||||
document.title = formattedAmount + ' - ' + vars.initialPageTitle;
|
|
||||||
|
|
||||||
vars.lastTotalHoldingsValue = totalValueGBP;
|
|
||||||
},
|
|
||||||
failure: function (response) { console.error("SetTotalHoldingsValue error: " + JSON.stringify(response)); }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//Create / update the currency allocation pie chart
|
//Create / update the currency allocation pie chart
|
||||||
function labelFormatter(label, series) {
|
function labelFormatter(label, series) {
|
||||||
return "<div class='pieLabel'>" + label + "<br/>" + Math.round(series.percent) + "%<br>" + formatAmount(series.data[0][1], 'GBP', 0) + "</div>";
|
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;
|
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() + '"> </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"> ' + dt + '</td></tr>\n' + //Spacer row
|
||||||
|
let rowsDef = '<tr id="' + subtableID + '_Summary" class="holdingsSubtotalRow"><td> </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's<br/>Gain £</th>' +
|
||||||
|
'<th>Today'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>' + ' ' + '</td>' + //Allocation
|
||||||
|
getPercentCell('#280ea6' /*'#3f2d95'*/, 70, scaledAllocation, allocationPercent.toFixed(1) + '%') + //Allocation
|
||||||
|
'<td class="num ' + todaysProfitOrLoss + '">' + ((todaysGainGBP == 0) ? ' ' : formatAmount(todaysGainGBP, 'GBP', 0)) + '</td>' + //Today's Gain £
|
||||||
|
'<td class="num ' + todaysProfitOrLoss + '">' + ((todaysGainPct == 0) ? ' ' : 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> </td>' +
|
||||||
|
'<td class="num ' + (todaysGain < 0 ? 'loss' : 'profit') + '">' + (todaysGain == 0 ? ' ' : formatAmount(todaysGain, 'GBP', 0)) + '</td>' +
|
||||||
|
'<td> </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> </td>' +
|
||||||
|
'<td class="num ' + (todaysGain < 0 ? 'loss' : 'profit') + '">' + (todaysGain == 0 ? ' ' : formatAmount(todaysGain, 'GBP', 0)) + '</td>' +
|
||||||
|
'<td> </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> </td>' +
|
||||||
|
'<td class="num">' + formatAmount(totalCashGBP, 'GBP', 0) + '</td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </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> </td>' +
|
||||||
|
'<td class="num">' + formatAmount(grandTotalGBP, 'GBP', 0) + '</td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </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: </span>' + '<span style="font-size: large">' + formattedAmount + '</span> ';
|
||||||
|
holdingsHTML += '<span style="font-size: small;"><span class="' + (vars.previousClose < totalValueGBP ? 'profit">+' : 'loss">') + formatAmount(totalValueGBP - vars.previousClose, "GBP", 0) + '</span> (d)';
|
||||||
|
holdingsHTML += ' / <span class="' + (vars.previousWeekClose < totalValueGBP ? 'profit">+' : 'loss">') + formatAmount(totalValueGBP - vars.previousWeekClose, "GBP", 0) + '</span> (w)</span>';
|
||||||
|
$("#spnTotalHoldings").html(holdingsHTML);
|
||||||
|
//Set the page title
|
||||||
|
document.title = formattedAmount + ' - ' + vars.initialPageTitle;
|
||||||
|
|
||||||
|
vars.lastTotalHoldingsValue = totalValueGBP;
|
||||||
|
},
|
||||||
|
failure: function (response) { console.error("SetTotalHoldingsValue error: " + JSON.stringify(response)); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
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() + '"> </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's<br/>Gain £</th>' +
|
||||||
|
'<th>Today'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>' + ' ' + '</td>' + //Allocation
|
||||||
|
getPercentCell('#280ea6' /*'#3f2d95'*/, 70, scaledAllocation, allocationPercent.toFixed(1) + '%') + //Allocation
|
||||||
|
'<td class="num ' + todaysProfitOrLoss + '">' + ((todaysGainGBP == 0) ? ' ' : formatAmount(todaysGainGBP, 'GBP', 0)) + '</td>' + //Today's Gain £
|
||||||
|
'<td class="num ' + todaysProfitOrLoss + '">' + ((todaysGainPct == 0) ? ' ' : 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> </td>' +
|
||||||
|
'<td class="num ' + (todaysGain < 0 ? 'loss' : 'profit') + '">' + (todaysGain == 0 ? ' ' : formatAmount(todaysGain, 'GBP', 0)) + '</td>' +
|
||||||
|
'<td> </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> </td>' +
|
||||||
|
'<td class="num">' + formatAmount(totalCashGBP, 'GBP', 0) + '</td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </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> </td>' +
|
||||||
|
'<td class="num">' + formatAmount(grandTotalGBP, 'GBP', 0) + '</td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </td>' +
|
||||||
|
'<td> </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's Gain</th>' +
|
||||||
|
'<th>Today's Gain %</th>' +
|
||||||
|
'<th>Since Last Sold<th>' +
|
||||||
|
'</tr></thead><tbody id="tbWatchlist"></tbody></table>';
|
||||||
|
|
||||||
|
$('#watchlistTableDiv').html(tbl);
|
||||||
|
|
||||||
|
$("#tblWatchlist").tablesorter({ ignoreCase: false });
|
||||||
|
$("#tblWatchlist").trigger("sorton", Cookies.get('sortWatchlist'));
|
||||||
|
$('#tblWatchlist').on('sortEnd', function (event) {
|
||||||
|
// Prints the current sort order to the console
|
||||||
|
Cookies.set('sorWatchlist', event.target.config.sortList, { 'sameSite': 'strict' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
117
Websites/SharePrices/SharePrices/scripts/SharePrices_Ledger.js
Normal file
117
Websites/SharePrices/SharePrices/scripts/SharePrices_Ledger.js
Normal 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']
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user