Shares portoflio buying wizard
My wife and I have an investment portfolio, that we manage on a platform called SelfWealth. We have a target portfolio, with certain investment products at certain percentages.
Whenever I need to go and buy a new batch of shares, I find there's a bit of friction in the process where I need to do a lot of manual calculations to figure out how many units of each share I need to buy. This friction sometimes leads be to procrastinate and put it off.
I had some free time, and I spent some time building a script to help me.
Get current state
The first thing to is to get my portfolio down from the website, and onto a table.
SelfWealth (as far as I can tell) didn't have a function to let me download my portfolio into a .csv, so I wrote up some lazy JavaScript scripts to scrape the page.
I didn't do anything clever to fully scrape the whole table. Instead, I wrote three scripts to scrape three columns as linebreak-separated text, and load that text onto the clipboard, which I can then manually paste into a .csv in Excel. I put each of these scripts into a bookmarklet on my browser.
To rip the share codes:
javascript:( function () { var tags = Array.from(document.querySelectorAll('tbody.holdings > tr > td:first-child')); tags.pop(); var out = tags.map(e => e.innerText).join('\n'); navigator.clipboard.writeText( out )} )()
To rip the number of units we hold:
javascript:( function () { var tags = Array.from(document.querySelectorAll("[data-bind='eightDigitText: totalUnits']"));tags.map(e=>e.innerText); var out = tags.map(e => e.innerText).join('\n'); navigator.clipboard.writeText( out ) } )()
To rip the unit price for each:
javascript:( function () { var tags = Array.from(document.querySelectorAll("[data-bind='posNeg: percentChange']")); var out = tags.map(e=>e.innerText.trim()).join('\n'); navigator.clipboard.writeText( out ); } )()
Figure out future state
Once I have a file, dated with today's date, I run this PowerShell script, which tells me how many units I need to buy of each share, to make our portoflio line up with our target.
function sum($array) { return ($array | Measure-Object -sum).Sum }
# initialise how much to spend on buying new shares, user input
# $cashToSpend = Read-Host "How much cash to spend?"
$cashToSpend = 32000
# get the target portfolio composition
$targets = import-csv .\target.csv | ? { $_.Target -ne 0 }
Write-Host Sum of targets $((sum $targets.Target)).ToString('P2')
# get the most current snapshot of portfolio
$current = import-csv (dir .\snapshots\*csv | Sort-Object | Select-Object -Last 1)
$current = $current |
% {
[float] $units = $_.Units
[float] $price = $_.Price
$value = $units * $price
$_ | Add-Member -NotePropertyName 'Value' -NotePropertyValue $value
$_
}
$currentTotal = sum $current.Value
$newTotal = $cashToSpend + $currentTotal
$current = $current |
% {
$code = $_.Code
$value = $_.Value
$percentage = $_.Value / $currentTotal
[float] $targetPercentage = ($targets | ? { $_.Code -eq $code }).Target
[float] $targetValue = $targetPercentage * $newTotal
$difference = $targetValue - $value
$valueToBuy = if ($difference -gt 0) { $difference } else { 0 }
$_ | Add-Member -NotePropertyName 'Percentage' -NotePropertyValue $percentage
$_ | Add-Member -NotePropertyName 'Target Percentage' -NotePropertyValue $targetPercentage
$_ | Add-Member -NotePropertyName 'Target Value' -NotePropertyValue $targetValue
$_ | Add-Member -NotePropertyName 'Value to Buy' -NotePropertyValue $valueToBuy
$_
}
$valueToBuyTotal = sum $current.'Value to Buy'
$projectedOverspend = $valueToBuyTotal - $cashToSpend
$current = $current |
% {
$valueToBuy = $_.'Value to Buy'
$_.'Value to Buy' = $valueToBuy - ($valueToBuy/$valueToBuyTotal * $projectedOverspend)
$unitsToBuy = [Math]::Floor($_.'Value to Buy' / $_.Price)
$_ | Add-Member -NotePropertyName 'Units to Buy' -NotePropertyValue $unitsToBuy
$_ | Add-Member -NotePropertyName 'Cash to Spend' -NotePropertyValue $($unitsToBuy * $_.Price)
$_ | Add-Member -NotePropertyName 'Value after Purchase' -NotePropertyValue $($_.'Cash to Spend' + $_.Value)
$_
}
$finalTotal = sum $current.'Value after Purchase'
$current = $current |
% {
$_ | Add-Member -NotePropertyName 'Final Percentage' -NotePropertyValue $($_.'Value after Purchase' / $finalTotal)
$_
}
$transactions = ($current | ? { $_.'Units to Buy' -ne 0 }).Count
# SelfWealth has $9.50 brokerage fee for every trade
$brokerage = 9.5 * $transactions
$current |
Export-Csv ".\out\$(get-date -Format "yyyy-MM-dd") out.csv"
$current |
Select-Object 'Code', 'Units to Buy', 'Final Percentage'
Write-Host Sum of holdings `$ $(sum $current.Value).ToString('N2')
Write-Host Cash to spend `$ $(sum $current.'Cash to Spend').ToString('N2')
Write-Host Left over `$ $($cashToSpend - (sum $current.'Cash to Spend') - $brokerage).ToString('N2') `(after brokerage $($brokerage.ToString('C2')) for $transactions trades`)