From 12db4a6b28ba50312255e91ad14c57e5a722988c Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 13 Oct 2023 16:58:51 +0100 Subject: [PATCH] submission --- .gitignore | 1 + README.md | 97 ++++++++++++++++++++++++++++++++---------------------- main.py | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 40 deletions(-) create mode 100644 .gitignore create mode 100644 main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env diff --git a/README.md b/README.md index 9e6c192..93b631b 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,57 @@ -# API-Stock-Signalling -###### NOTE: this is a project to practice your Python industry skills. Please upload your solutions by opening a new branch to the main. All files that are instantly merged to the main will be deleted. -## Overview -A small consultancy who is currently delivering client work for a major bank wants to monitor the prices for the following stocks: Tesla, Apple, Microsoft, Google, and Nike. Once the stock falls below a certain price, they want to be immediately notified so that they can purchase more stocks. -## Goal -Write a real time Python application that monitors the prices then notifies the user when the price of any of these stocks falls by at least £0.25 GBP. The user should also be notified if today's price is less than the 7-day average of that stock price. -## Brief -To do this, the client has recommended using a popular automation website called IFTTT: https://ifttt.com/. They are willing to use a different provider or solution (use those consultancy skills!) if you believe there is a better option. -#### IFTTT Applet - -IFTTT stands for “If This Then That” and it’s an automation platform that allows you to connect different apps and services together. An applet is a connection between two or more apps or devices that enables you to do something that those services couldn’t do on their own. Applets consists of two parts triggers and actions. Triggers tell an applet to start, and actions are the end result of an applet run. To use an applet, you’ll need to create a free IFTTT account and connect your apps and devices to IFTTT so that they can talk to each other. - -#### Proposed Workflow -- Write a script that returns the stock prices. This will involve making an API request. -- Set up a IFTTT account and applet. This will be accessible via the mobile app which allows you to trigger the webhook service provided by IFTTT. -- You will need to configure the 'webhooks' service to receive web requests. You can find more details here: https://ifttt.com/maker_webhooks. -- From here you can write an application that utilises the requests package to make POST and GET requests. -- Think of what aspects or components of your proposed solution needs to be tested and what would these tests look like and attempt to implement such tests. - -## Main Considerations -- Choose an automation approach. Are you planning on using IFTTT or another workflow? -- What API are you going to use? You can use this as a starting point: https://github.com/public-apis/public-apis -- Remember, this is a proposed workflow. If you believe you have a more efficient approach please reach out to the Academy Team. -#### Requirements Gathering -The start to any project is to make sure you have clear and well-defined requirements for your project. Most projects start with a vague idea of what the stakeholder wants, and as a consultant, we will never have as much knowledge about their problem/business context as they do. Therefore, we need to get as much information out of them as possible, as they will subconsciously assume that we know everything. For this project, Alex Naylor will be the stakeholder. - -If you don't know the answer to any question then you should always ask - NEVER ASSUME. This will only risk the accuracy of your work and end up having to do everything all over again if you wrongly assume. - -Questions to ask yourself constantly throughout the project are: - -- What is the purpose of this project, why does the stakeholder want this and what is the desired outcome of the project? -- Is there any extra info that the stakeholder could tell you to help tailor the project to what they want? - -## Assessment -For the assessment, you will have a 15 minute technical interview. This will consist of a strict 5 minute presentation on your technical solution. There is no need to create slides for this but you may want to demo your code. For the second half of the session, you will be asked technical questions related to the project. You will be assessed on: -- Project Complexity -- Brief Completness i.e. have you managed to meet the client brief? -- Coding Standards - -Good Luck! +# First things first +1. "AAPL", "MSFT", "GOOGL", "TSLA", "NKE" and 0.25 are default settings for companies and drop level. You can change those at your own discretion. +2. Create .env file locating it in your current working directory +3. Make sure .gitignore has .env +4. Inside .env set your email details: + +EMAIL_USER=here_is_your_gmail_account@gmail.com +RECEIVER_EMAIL=here_is_your_gmail_account@gmail.com +EMAIL_PASS=here past your pass (see below) + +5. Proceed with adding password inside .env following the procedure below + +# Create & use app passwords + +To help keep your account secure, use "Sign in with Google" to connect apps to your Google Account + +1. Go to your Google Account. +2. Select Security. +3. Under "How you sign in to Google," select 2-Step Verification. +4. At the bottom of the page, select App passwords. +5. Enter a name that helps you remember where you’ll use the app password. +6. Select Generate and follow the instructions on your screen. The app password is the 16-character code that generates on your device. + +Paste it in .env file replacing here past your pass with whatever 16-character you got: + +EMAIL_PASS=here past your pass + +# Stock exchange working hours +Our example is looking at "AAPL", "MSFT", "GOOGL", "TSLA", "NKE", which are listed in US. Since stock exchange is not trading 24/7, our watcher will see no change during closed time. +You could replace the tickers with any other companies listed at Stock Exchanges located in working time zones, just double-check at Yahoo finance the ticker is correct. + + +# Why smtplib over IFTTT +Following YAGNI principle we believe the requirement of sending notification via email provides exactly what we need. For this task smtplib offers more direct and controllable solution, also it is free. + +Considering further versions, IFTTT would be chosen for additional web notification solutions, such as Facebook, Telegram, SMS etc. + +# Why yfinance over API +yfinance is a library for fetching market data from Yahoo Finance. It provides a simpler interface for fetching stock data compared to making HTTP requests to an API, parsing the responses, and handling errors. + +Given that we are planning to run the script every minute, this would total to a max of 1,440 requests per day (assuming one request per run). This falls well within the documented rate limits for the public API based on IP address (48,000 requests per day) + +# Create & use app passwords + +To help keep your account secure, use "Sign in with Google" to connect apps to your Google Account + +1. Go to your Google Account. +2. Select Security. +3. Under "Signing in to Google," select 2-Step Verification. +4. At the bottom of the page, select App passwords. +5. Enter a name that helps you remember where you’ll use the app password. +6. Select Generate and follow the instructions on your screen. The app password is the 16-character code that generates on your device. + +Paste it in .env file replacing here past your pass with whatever 16-character you got: +EMAIL_PASS=here past your pass + +"AAPL", "MSFT", "GOOGL", "TSLA", "NKE" \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..cc79b3e --- /dev/null +++ b/main.py @@ -0,0 +1,90 @@ +""" +This script monitors specified stocks, compares their prices with historical averages, +and sends email notifications when certain conditions are met. +""" + +#Import the necessary libraries and modules +import yfinance as yf +import smtplib +import schedule +import time +import os +from forex_python.converter import CurrencyRates +from dotenv import load_dotenv +from statistics import mean + +# Load environment variable from .env file +load_dotenv() + +# Initialize currency converter +currency_converter = CurrencyRates() + +# Define the stocks to monitor and the email details +stocks_to_monitor = ["AAPL", "MSFT", "GOOGL", "TSLA", "NKE"] +previous_prices = {ticker: 0 for ticker in stocks_to_monitor} + +#Set-up your own environment .env (README) +email_user = os.environ.get('EMAIL_USER') +receiver_email = os.environ.get('RECEIVER_EMAIL') + +#Error Handling (in case user forgets setring-up .env) +try: + email_pass = os.environ['EMAIL_PASS'] +except KeyError: + print("EMAIL_PASS environment variable not found. Check README") + exit(1) + +# Return a yfinance Ticker object for the given symbol +def process_tickers(): + for ticker in stocks_to_monitor: + stock = yf.Ticker(ticker) # Directly instantiate yf.Ticker within the loop + check_seven_day_avg(stock, ticker) + check_one_minute_price_change(stock, ticker) + +# Handling the price retrieval and conversion to GBP +def get_current_price(stock): + current_price_usd = stock.info['currentPrice'] + current_price_gbp = currency_converter.convert('USD', 'GBP', current_price_usd) + return current_price_gbp + +# Fetch the last 7 days of price data, calculate the average and prepare message +def check_seven_day_avg(stock, ticker): + hist = stock.history(period="7d") + avg = mean(hist["Close"]) + avg_gbp = currency_converter.convert('USD', 'GBP', avg) + + current_price_gbp = get_current_price(stock) + if current_price_gbp < avg_gbp: + subject = f'Price Alert for {ticker}' + body = f'The current price of {ticker} is below the 7-day average, at {current_price_gbp} GBP.' + send_notification(subject, body) + +def check_one_minute_price_change(stock, ticker): + current_price_gbp = get_current_price(stock) + previous_price_gbp = currency_converter.convert('USD', 'GBP', previous_prices[ticker]) + diff = previous_price_gbp - current_price_gbp + if diff >= 0.01: + subject = f'Price Drop Alert for {ticker}' + body = f'The price of {ticker} has dropped to {current_price_gbp} GBP.' + send_notification(subject, body) + + # Store the price in USD for the next check + previous_prices[ticker] = stock.info['currentPrice'] # Fetching the current price in USD again. + +# Send an email notification +def send_notification(subject, body): + try: + with smtplib.SMTP('smtp.gmail.com', 587) as server: + server.starttls() + server.login(email_user, email_pass) + msg = f'Subject: {subject}\n\n{body}' + server.sendmail(email_user, receiver_email, msg) + except Exception as e: + print(f"Error sending email: {e}") + +# Schedule the process_tickers function to run every minute +schedule.every(1).minutes.do(process_tickers) + +while True: + schedule.run_pending() + time.sleep(1) \ No newline at end of file