I know that there is a couple of similar How-Tos.
But they all use templates and input numbers to track stock and I guess that is fine for a couple of sensors. I decided to go down different route.
I use Yahoo Finance HACS integration - so make sure you have that configured. Follow the github documentation for that integration how to install it and set it up.
Next to track current value for the shares I hold, gain/loss % gain/loss and trend vs buy price (up/down/break_even) and total portfolio value I use a home assistant script
In your config folder, go to /config/python_scripts
Create the folder if it doesn’t exist
Save the following file as:
portfolio_tracker.py
portfolio = {
"apple": {
"sensor": "sensor.yahoofinance_aapl",
"shares": 5,
"buy_price": 150.00,
"price_in_pence": False
},
"barclays": {
"sensor": "sensor.yahoofinance_barc_l",
"shares": 100,
"buy_price": 1.50,
"price_in_pence": True
}
# Add more stocks here...
}
total_value = 0.0
total_gain = 0.0
for key, data in portfolio.items():
sensor = data["sensor"]
shares = float(data["shares"])
buy_price = float(data["buy_price"])
entity = hass.states.get(sensor)
if entity is None or "regularMarketPrice" not in entity.attributes:
continue
raw_price = float(entity.attributes["regularMarketPrice"])
current_price = raw_price / 100 if data.get("price_in_pence", False) else raw_price
value = round(current_price * shares, 2)
gain = round((current_price - buy_price) * shares, 2)
gain_pct = round(((current_price - buy_price) / buy_price) * 100, 2)
if abs(current_price - buy_price) < 0.01:
trend_vs_buy = "break_even"
elif current_price > buy_price:
trend_vs_buy = "up"
else:
trend_vs_buy = "down"
total_value += value
total_gain += gain
hass.states.set(f"sensor.{key}_portfolio", value, {
"friendly_name": f"{key.replace('_', ' ').title()} Value",
"current_price": current_price,
"shares": shares,
"buy_price": buy_price,
"gain": gain,
"gain_pct": gain_pct,
"trend_vs_buy": trend_vs_buy,
"unit_of_measurement": "GBP"
})
# Total Portfolio Sensor
total_value = round(total_value, 2)
total_gain = round(total_gain, 2)
total_gain_pct = round((total_gain / (total_value - total_gain) * 100), 2) if total_value - total_gain > 0 else 0
hass.states.set("sensor.portfolio_total", total_value, {
"friendly_name": "Total Portfolio Value",
"gain": total_gain,
"gain_pct": total_gain_pct,
"unit_of_measurement": "GBP"
})
At the top of the script in the portfolio section populate your stock holdings
sensor → from the yahoo finance integration
shares → this is where you enter how many shares you have
buy_price → this is the value average price per share that you have paid for your shares
price_in_pence → is needed for UK stocks (Yahoo Finance returns prices in pennies).
Save the script and restart HA. Once the script is saved you won’t have to restart HA in case of modifications - just go to dev tools and click reload Python Scripts.
Next step is to create Automation to run that script:
alias: Update Stock Portfolio
trigger:
- platform: time
at: "08:00:00"
action:
- service: python_script.portfolio_tracker
mode: single
I run mine every couple of minutes
With that you should have 2 sensors for each stock i.e. for Rolls Royce:
sensor.yahoofinance_rr_l (from Yahoo Finance integration)
sensor.rolls_royce_portfolio (from the python script)
As for the dashboard - you can do whatever you like with the data - I keep mine simple:

I’m using Mushroom Template cards:
type: custom:mushroom-template-card
primary: >
ÂŁ{{ (state_attr(entity, 'regularMarketPrice') / 100) | round(2) }} ({{
state_attr(entity, 'regularMarketChangePercent') | round(2) }}%)
secondary: >
52W Range: ÂŁ{{ (state_attr(entity, 'fiftyTwoWeekLow') / 100) | round(2) }} -
ÂŁ{{ (state_attr(entity, 'fiftyTwoWeekHigh') / 100) | round(2) }}
icon: |
{{ state_attr(entity, 'icon') }}
icon_color: |
{% if state_attr(entity, 'trending') == 'up' %}
green
{% elif state_attr(entity, 'trending') == 'down' %}
red
{% else %}
grey
{% endif %}
multiline_secondary: false
fill_container: false
grid_options:
columns: 6
rows: 1
entity: sensor.yahoofinance_rr_l
tap_action:
action: more-info
and for the other tile:
type: custom:mushroom-template-card
primary: "Total: ÂŁ{{ states(entity) }}"
icon: mdi:cash-multiple
multiline_secondary: false
fill_container: false
grid_options:
columns: 6
rows: 1
entity: sensor.rolls_royce_portfolio
icon_color: |2-
{% set trend = state_attr(entity, 'trend_vs_buy') %}
{% if trend == 'up' %}
green
{% elif trend == 'down' %}
red
{% else %}
grey
{% endif %}
secondary: |2-
{% if state_attr(entity, 'trend_vs_buy') == 'break_even' %}
Break-even — no gain or loss
{% elif state_attr(entity, 'trend_vs_buy') == 'up' %}
Profit: ÂŁ{{ state_attr(entity, 'gain') }} ({{ state_attr(entity, 'gain_pct') }}%)
{% else %}
Loss: ÂŁ{{ state_attr(entity, 'gain') }} ({{ state_attr(entity, 'gain_pct') }}%)
{% endif %}
tap_action:
action: more-info
Also from the python script you get one more sensor called:
sensor.portfolio_total
it has the following attributes:
(No, those are not real values
I’m pleased to say that using home assistant/yahoo finance integration and my script I get better quality and more up-to-date values than using my Freetrade app
So here it is. Of course, feel free to modify the script to suit your own needs.
And just to be crystal clear:
You use this at your own risk. I accept no liability for any decisions or losses resulting from the use of this script or the data it generates.
In other words – do your own homework and make sure to double-check everything.
