How to schedule a Jupyter Notebook to email yourself stock tips

We’ve recently introduced a new feature in Deepnote that allows you to schedule a notebook on a daily or weekly basis. I’ve created a simple example to showcase how you can leverage that to send weekly emails with results of your analysis. You can use the same method to send yourself newsletters, train your models, or get a daily weather forecast every morning! 😄

You can find a ready-to-use notebook with all the code here. In the rest of this article, I will outline the important parts.

Inspired by this notebook, I am using the pandas datareader package to fetch daily stock quotes. For example, this is how you can fetch Microsoft’s stock quote for the past year:

from pandas_datareader import data
from datetime import datetime
end = datetime.now()
start = datetime(end.year-1, end.month, end.day)
df = data.DataReader('MSFT', 'yahoo', start, end)

df is a Pandas Dataframe containing daily stock quotes. I’ve decided that I want to compare the current price to 200 day moving average, so let’s calculate that:

last_200d = df.tail(200)
last_200d_average = last_200d["Close"].mean()
current_price = df.tail(1).iloc[0]["Close"]
diff = current_price - last_200d_average
diff_relative = (diff / current_price) * 100

diff_relative represents relative difference to the current price — if the value is negative, we consider the stock to be in a “dip”.

Since outputs from our simple analysis are ready, let’s continue with sending those as an email.

I would like to include a list and bold text in my email, which means we need to generate an HTML document instead just using plain text. I’m going to use the Jinja package as a template engine. Let’s try it out!

from jinja2 import Environment, PackageLoader
env = Environment()
template = env.from_string("""
<html>
<body>
Hello! Stocks in your portfolio comparing to 200 day moving average:<br>
<ul>
{% for result in results | sort(attribute='diff_relative') %}
<li>
<b>{{ result.ticker }}</b>{{"%.2f"|format(result.diff_relative)}}%
</li>
{% endfor %}
</ul>
</body>
</html>
""")
html = template.render(results=results)

The html variable contains ready to be sent content for our email. Now, how do we actually send email?

Sending emails programmatically and making sure they are delivered is not so straightforward. An SMTP server is required to send and receive emails. Since anyone on the Internet can do that and spam you with emails, email providers are doing what they can to prevent unwanted messages to reach you. That’s why, instead of creating our own SMTP server that will be basically untrusted, it’s better to leverage existing ones, such as Mailgun or Gmail.

For the sake of simplicity, let’s use Gmail because it’s familiar and simple to set up:

  1. Create a new Google account (e.g. beststockpredictions@gmail)
  2. Enable the Less secure apps settings for the account https://myaccount.google.com/u/2/lesssecureapps .This will enable us to authenticate ourselves using username and password from our code. For production applications, it’s better to use OAuth.

Let’s use our newly created Google credentials and html variable created in the previous step in the following snippet:

import smtplib, ssl
from email.mime.multipart import MIMEMultipart
emails = ['your.email@email.com', 'anotheremail@email.com']smtp_server = "smtp.gmail.com"
port = 587
sender_email = "yourcreatedaccount@gmail.com"
password = "replace-with-password"
context = ssl.create_default_context()message = MIMEMultipart("alternative")
message["Subject"] = "Stock dip detector"
message["From"] = sender_email
part = MIMEText(html, "html")
message.attach(part)
server = smtplib.SMTP(smtp_server, port)
server.starttls(context=context)
server.login(sender_email, password)
server.sendmail(sender_email, emails, message.as_string())

The beauty of notebooks is that you can go ahead and run this cell to verify the email is sent. However, this comes with a downside — you need to make sure you don’t send emails too often, otherwise it could be caught by spam filters.

What if we want to send an email on a daily or weekly basis? Here’s a quick tutorial on how you can schedule your local Jupyter notebook.

However, if you are looking for a more robust solution separate from your local machine, you need to schedule and run the notebook in the cloud. It’s possible to use your own deployment of Jupyter Lab (and use the Scheduler plugin) or use a managed notebook solution like Deepnote.

A view of Deepnote’s scheduling
A view of Deepnote’s scheduling
A view of Deepnote’s scheduling feature

If you are already using Deepnote, you can schedule a notebook right in the interface. It’s quite straightforward to do it on your own, but you can also check out the documentation. I would recommend to opt in to receive an email notification if your scheduled run has failed, since it’s helpful to know if something has broken.

I hope you’ve found this helpful. As I’ve mentioned in the beginning, feel free to duplicate the notebook to kickstart your new project!

Software Engineer @ Deepnote