Dyota's blog

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`) 

#javascript #powershell