Sending SMS alerts using Python
How I setup a monitor that reads my alert logs and sends texts so I can be lazy
This is a first attempt at writing about some of the tools I have built and projects I have undertaken in the course of my trading.
I would regard myself as an inherently lazy person and I often find myself building things that will enable future laziness. Learning to code and write python scripts has certainly helped in that endeavor.
Today I want to share a simple script to kickstart my substack journey. Bear with my horrible writing and simplistic code. I intend to get better at this.
The objective for this script are simple. I use a scanner to get alerts for stocks that fulfill certain criteria (Trade-Ideas in this case). Sometimes I have a few seconds to act upon those alerts (HTB short sellers where you at). I have audio alerts set and while Trade-Ideas claims to have an SMS alert feature for when I’m away from the computer, I have found them to be woefully inconsistent. After enough teeth gnashing and desk banging on returning to my desk to missed alerts that move beautifully in my favor (never missing those big losers, of course!), I decided it was time to implement my own solution.
Workflow
Trade-Ideas helpfully has the ability to write alerts to a diagnostic CSV file. Using this file, my general workflow is as follows (will break down the details with some python notebook code):
Read Trade-Idea’s diagnostic CSV output
Check if there are any new alert for the day and write to a list of unique alerts
For any new alerts in list, send an SMS to my phone with pertinent details
If desired, print alert to screen or print “no new alerts” as heartbeat
Wrap all this in a cronjob or a background scheduler so I can run this repeatedly during trading hours.
With this workflow I can constantly monitor a diagnostic output from any program and get a text on my phone (obviously using the free method from Gmail).
The Nitty Gritty
Read Trade-Ideas diagnostic CSV output
def get_todays_file(): today = date.today() file_path = "yourlogfilename.{}.csv".format(today.strftime("%Y%m%d")) try: alert_log = pd.read_csv(file_path) except FileNotFoundError: alert_log = [] print("No file yet") return alert_log
Easy enough to run pandas read_csv function, however, since I generate a new diagnostic file each day and I want to read todays file, I added a string format for today that will match the filename being output by Trade-Ideas. We also don’t want our program to fail in the morning before any alerts have been generated so gonna throw in an error exception if no file was found, wrap that in a function that returns the alert log and we are ready to start running some logic on this data.
Check if there are any new alert for the day and write to a list of unique alerts
def unique_alert(alerted, alert_log): symbol_alerts = [] if len(alert_log) > 0: firsts = alert_log.groupby('Symbol', as_index=False).first() alerted_reset = reset_alerted_daily(alerted) for symbol in firsts.Symbol: if symbol not in alerted_reset: alerted_reset.add(symbol) symbol_alert = firsts.loc[firsts.Symbol == symbol] [['Symbol', 'TimeStamp', 'Price', 'Volume Today']] symbol_alerts.append(symbol_alert) return symbol_alerts
Since we get numerous alerts for the same symbol each day, we want to store previously alerted symbols and only pass on new alerts for each day. We start with an empty list of alerts and pass in our alert_log
which we generated in the previous function and a stored list of the day’s previously alerted symbols. Once we have some data in our alert_log
, we use panda’s groupby
logic to group each symbol and keep the first alert only, if we where trading we would be acting on the first alert to trigger.
We then make sure the alerted list is reset, checking if its a new day using the reset_alerted_daily
function (I reset it at 7 am daily). This is in case we run this over multiple days.
def reset_alerted_daily(alerted):
now = datetime.now()
if ( now.hour == 7 and now.minute == 1 and now.second == 1 ) :
alerted = set()
return alerted
Then we iterate through all the first symbols we kept, check them against our alerted list and if it’s not in there already we add it to todays alerted list.
For any new alerts in list, send an SMS to my phone with pertinent details
import email, smtplib, ssl
from providers import PROVIDERS
def send_sms_via_email(
number: str,
message: str,
provider: str,
sender_credentials: tuple,
subject: str = "sent using etext",
smtp_server: str = "smtp.gmail.com",
smtp_port: int = 465,
):
sender_email, email_password = sender_credentials
receiver_email = f'{number}@{PROVIDERS.get(provider).get("sms")}'
email_message = f"Subject:{subject}\nTo:{receiver_email}\n{message}"
with smtplib.SMTP_SSL(
smtp_server, smtp_port, context=ssl.create_default_context()
) as email:
email.login(sender_email, email_password)
email.sendmail(sender_email, receiver_email, email_message)
def send_sms(message_str:str):
number = "Your Phone number here (e.g. 1233211234)" #edit this string
message = message_str
provider = "your phone provider here(e.g. T-Mobile)" #edit this string
sender_credentials = ("youremail@gmail.com", "SMTP forwarding password") #edit these two strings
send_sms_via_email(number, message, provider, sender_credentials)
I’ve included the send_sms
functions here but for more info on setting up some free SMS alerts for other implementations, check out this guys you-tube video. He does a good job at walking through the steps necessary to set up free SMS forwarding through Gmail.
If desired, print alert to screen or print “no new alerts” as heartbeat.
def send_alert_text(alerted): alert_log = get_todays_file() alerts = unique_alert(alerted, alert_log) for alert in alerts: send_sms(alert) print(alert) if len(alerts) < 1: print("no new alerts found {}".format(datetime.now()))
As you can see I’ve wrapped our previous functions into this send_alert_text
function. For each new alert generate by the unique_alerts
function we will send a text with the pertinent information and print to our terminal, if new new alerts are found we will note that with a timestamp. Note that unique_alerts
will only return an item if there is a new alert for the day.
Wrap all this in a cronjob or a background scheduler so I can run this repeatedly during trading hours.
from apscheduler.schedulers.background import BackgroundScheduler def run_alert_log_background(): ALERTED = set() sched = BackgroundScheduler() sched.add_job(send_alert_text, 'cron', day_of_week='mon-fri', hour='7-16', second='*/10',args=[ALERTED] ) sched.start()
I use apscheduler to run asynchronous jobs in the background. I initiate ALERTED as an empty set the first time and then set a job to run my send_alert_text
function with the alerted arg passed in.
I’ve written this all in a Jupyter notebook on my GitHub which you can check out here. In practice I run this as a python script in PowerShell with send_sms
modules imported. This is script is quite simple and should be pretty straight forward to use but feel free to ask any questions in the comments.
Have fun with this one and I hope it was useful to someone out there!
Cool use case, thanks for the share!
Love this!