Building a Telegram bot in Python to track your portfolio

In the past months, I’ve been spending a lot of time researching about investing: building your portfolio, back testing it, tracking its performance, and acting accordingly. One of these endeavors (and my stubbornness to build my own tools) has led me to create a telegram bot, here’s a small write up in hope you'll build your own.

A Telegram Bot? Have you heard of Yahoo Finance?

Yes, I have actually heard about and used Yahoo Finance to track my portfolio, but there are a couple of reasons why I think it doesn't quite cut it:

Given these constraints (and my stubbornness to build my own tools), I decided I needed some sort of "programmable" notification system. Having heard a bit about telegram/discord bots, and being a subscriber to a couple of telegram channels myself, I decided to give Telegram a shot.

Building a basic bot

I can't say I was surprised to see a great Python wrapper written exactly for this purpose: python-telegram-bot. Can't tell you how easy it was to set everything up using their extensive Wiki.

Let me walk you a bit through the process. You start by installing the library:

$ pip3 install python-telegram-bot 

Once that's up an running, I created a simple "shell" bot that responds whenever you send the message /start, let's call it bot.py:

# bot.py
from telegram.ext import (
    Updater,
    CommandHandler,
    MessageHandler,
    Filters,
    CallbackContext,
)
from telegram import Update

# define your API token: https://core.telegram.org/bots#3-how-do-i-create-a-bot 
API_TOKEN = "YOUR API TOKEN" 

# what your bot should reply when we send the /start command
def start(update: Update, context: CallbackContext):
    update.message.reply_text("Welcome :)")

# the main function, with some boilerplate
def main():
    updater = Updater(API_TOKEN, use_context=True)
    dispatcher = updater.dispatcher
    dispatcher.add_handler(CommandHandler("start", start)) # this line is what matters most
    updater.start_polling()
    updater.idle()

if __name__ == "__main__":
    main()

If you now navigate back to your terminal and simply run this script by doing python bot.py, you'll see that something is running in your terminal. Then, when you message your telegram bot, you get a nice little prompt back with "Welcome :)".

Getting information on your portfolio (example)

But we want to get a little more fancy, ideally, as an example, let's say that we want our bot to inform us on our portfolio worth. We create a portfolio.py file:

# portfolio.py
import yfinance as yf # pip install yfinance

# you bought 2 stocks of Microsoft
ACQUISITION_PRICE = 150
ACQUISITION_QTTY = 2
STOCK_NAME = "MSFT"

# a function that returns our gains on the purchase of MSFT stocks
def get_current_value():
    msft = yf.Ticker("MSFT")
    last_value = yf.info.get("previousClose")

    previous_worth = ACQUISITION_QTTY * ACQUISITION_PRICE 
    current_worth = ACQUISITION_QTTY * last_value
    gain = current_worth - previous_growth

    return f"Your current gains on {STOCK_NAME}: {gain:.2f}"

The above function, get_current_value will inform on how much your Microsoft stock is worth at any point in time, from when you purchased it.

Integrating financial updates with our Telegram bot

Now that we have both a telegram bot and a basic script to update us on our portfolio performance, it's time to bring both together.

To do so, we create two functions:

# ... the rest of your bot.py file

def alarm(context): # FIRST IMPORTANT ADDITION
    job = context.job # get context
    text = get_current_value() # run our financial update
    context.bot.send_message(job.context, text=text) # respond with the string returned by the function

def set_update(update: Update, context: CallbackContext): # SECOND ADDITION
    chat_id = update.message.chat_id # get our chat ID
    secconds_between_runs = 24 * 7 * 60 * 60  # run every week (24 hours * 7 days * 60 mins * 60 secs)

    update.message.reply_text("Financial updates will run!") # confirm to user that it will run
    context.job_queue.run_repeating(
        alarm, interval=secconds_between_runs, first=30, context=chat_id
    ) # run the alarm as a recurring update and the first message runs 30 seconds after our update is set

def main():
    updater = Updater(API_TOKEN, use_context=True)
    dispatcher = updater.dispatcher
    dispatcher.add_handler(CommandHandler("start", start))
    dispatcher.add_handler(CommandHandler("set_update", set_update)) # NEW COMMAND
    updater.start_polling()
    updater.idle()

if __name__ == "__main__":
    main()

After this, every time you send your bot the message /set_update, it will schedule our financial update to be run every week! (feel free to try it live by making it run every 10 minutes or so)

Adding a bit of security and deploying

We don't want the whole world to access our bot (or our financial updates), so, it's smart to limit your bot to respond only to yourself (or your telegram username). And easy way to do so is to add an if statement to your set_update function like so:

def set_update(update: Update, context: CallbackContext):
    ...
    if username == AUTHORIZED_USERNAME:
        update.message.reply_text("Financial updates will run!")
        context.job_queue.run_repeating(
            alarm, interval=every_seconds, first=30, context=chat_id
        )
    else:
        update.message.reply_text(
            "Sorry, but you are not authorized to use this command.."
        )

This ensures your bot only gives financial updates to YOU, which is pretty important..

For deployment, you only need to leave your bot.py script running somewhere. I chose to use one of my Virtual Private Servers and run the script in a tmux session. But there are also some good alternatives in the docs if you re interested!

Closing thoughts

This was a very simple example of how you can get your bot up and running really quickly. But some things differ from the setup that I have actually in place (and the one you'll probably build):

This is why hacking on your own tools is more rewarding than just using an off the shelf app: Yes, it takes time, and yes, it can seem a lot less "fancy". But you learn a lot while doing it, and more importantly: you build it to become YOUR tool, not A tool.


Thanks for reading! If you like this post, consider reaching out! I'm thinking of starting a newsletter, and I'm interested in knowing if you would potentially sign up!

November 23, 2020