Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions historical_stock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import requests
import json


def get_data_request(ticker):
"""
This function fetches the api based on what company the ticker
presents.
Afterwards it gets the dictionary that has all the records of daily
prices per day.
Then, it makes sure to only take 5 records and returns the final dictionary.
"""
url = f"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&
symbol={ticker}&outputsize=compact&apikey=demo{api-key}"
response_for_historical = requests.get(url)
if response_for_historical.status_code == 200:
data = response_for_historical.json()
main_dict = data['Time Series (Daily)']
finalized_dict = finalize_dict(main_dict)
return finalized_dict
else:
print("Did not fetch data properly, ",
response_for_historical.status_code)



def store_weekly_avgs(ticker_list):
"""
This function calculates the weekly average of a given
dataset then assigns it to its related symbol (company)
in form of a dictionary.
"""
week_avgs = []
for ticker in ticker_list:
dataset = get_data_request(ticker)
week_avgs.append(calc_weekly_average(dataset))
return {k: v for k, v in zip(ticker_list, week_avgs)}


def finalize_dict(dict):
"""
This function returns the first 5 items of the given
dictionary for its weekly calculation.
The reason 5 is because the stock market is only open
on weekdays.
"""
first_five_items = {k: dict[k] for k in list(dict)[:5]}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we sure there will always be at least 5 items in the dictionary? What if there are less than 5, will it raise an error?

return first_five_items

def return_open_val(given_dict):
# returns the open price of a given dictionary
return float(given_dict['1. open'])

def calc_weekly_average(given_dict):
# calculates the weekly average of a given dictionary
total = 0
for key, value in given_dict.items():
total += return_open_val(value)
return total/5
Copy link

@pgrach pgrach Oct 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function finalize_dict always grabs the first 5 entries from the returned data. I wonder if this may make your average in calc_weekly_average inaccurate because you're always dividing by 5 (If the market was closed for a holiday within the last 5 weekdays)?


def latest_stock_lt_week_avg(stock_price, weekly_average):
"""
This function returns True if the latest stock price is less
than it's weekly average.
"""
if stock_price < weekly_average:
return True

def main():
"""
The main function triggers the functions above then
communicates with IFTTT by posting a request to trigger
an email to the assigned Gmail accounts with the IFTTT
"""
api_key = "nmSjoGFt5z5ExJ4bLUMBcr0JozQykoHC2TB5dUNdUT4" # my api key for authentication
Copy link

@pgrach pgrach Oct 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security: Your API keys are hardcoded in your code. .env + .gitignore would help to avoid committing your key to the public repo.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a really good point, thank you

event_name = "stock_update" # needed for my IFTTT trigger
url = f"https://maker.ifttt.com/trigger/{event_name}/with/key/{api_key}" # the api

TICKER_LIST = ["AAPL","TSLA","MSFT","GOOGL","NKE"] # symbol list stakeholder queried
weekly_avgs = store_weekly_avgs(TICKER_LIST)
json_file = "real_time_stock_data" # name of json file
recent_stock_prices = []

try:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error Handling future suggestions: if an API call fails, you might want to retry it a few times before giving up.

with open(json_file, 'r') as f:
data = json.load(f)
for key,value in data.items():
recent_stock_prices.append([value][0])
for i in range(len(weekly_avgs)):
if latest_stock_lt_week_avg(recent_stock_prices[i],weekly_avgs[i]):
message = f"The stock price for {key} are less than
the stock's weekly average. Buy more stocks now!!!"
data = {
'Value1':message
}
response = requests.post(url, json=data)
if response.status_code == 200:
print("Email sent!")
else:
print("Failed to send email.")
print("And yes, there has been a drop of prices")

except FileNotFoundError:
print(f"The file '{json_file}' was not found.")
except json.JSONDecodeError:
print(f"The file '{json_file}' is not a valid JSON file.")


148 changes: 148 additions & 0 deletions real_time_stock_prices.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import requests
import json

def get_dataset(ticker):
"""
A function that fetches an api based on ticker value (sybmbol of company) whic
then returns a dictionary data structure
"""
print("Running dataset")
response_for_realtime = requests.get(f"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol={ticker}&interval=1min&outputsize=compact&apikey={api-key}")
if response_for_realtime.status_code == 200:
data = response_for_realtime.json()
print(data)
return data

else:
print("Error getting the request needed: ", response_for_realtime.status_code)

def get_main_data(dict):
"""
A simple function that returns data belonging to the key 'Time Series (1min)
"""
print("Getting main data")
return dict['Time Series (1min)']

def get_datasets(ticker_list):
"""
A function that takes in a list of tickers and calls the
function 'get_dataset()' and stores each dataset on to a list
then returns it.
"""
print("Running datasets")
datasets = []
for ticker in ticker_list:
temp_data = (get_dataset(ticker))
datasets.append(get_main_data(temp_data))
return datasets

def store_stock_price(stock_pair,new_price):
"""
A functions that updates the prices.
This function would be called when there has been an update on the prices.
"""
stock_pair.pop(0)
stock_pair.append(new_price)
return stock_pair

def has_price_decreased(pair_of_stock_prices):
"""
This function checks if the new stock price is below £0.25.
The function will return True.
This is called from the main function.
"""
if float(pair_of_stock_prices[1]) < (float(pair_of_stock_prices[0]) - 0.25):
return True


def generate_stock_queue(dict):
"""
This function generates a pair of stock prices.
It acts as a queue where when you push one price, then
push the most recent price on the list.
It then converts to GBP which is what the stakeholder wants.
"""
new_stock_pair = []
latest_data_dict = list(dict.items())
newest_price = latest_data_dict[0][1]['1. open']
second_newest_price = latest_data_dict[1][1]['1. open']
new_stock_pair.append(convert_usd_to_gbp(newest_price))
new_stock_pair.append(convert_usd_to_gbp(second_newest_price))
return new_stock_pair

def get_all_stock_pairs(datasets):
"""
This function iterates each dataset to generate a stock pair
for each company.
It returns a two-dimensional array.
"""
two_d_array_stock = []
for dataset in datasets:
two_d_array_stock.append(generate_stock_queue(dataset))
return two_d_array_stock


def return_open_val(given_dict):
# returns the open price for a given dataset
return float(given_dict['1. open'])



def convert_usd_to_gbp(price_to_convert):
# a simple function to convert USD to GBP
return price_to_convert/1.23
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've hardcoded the conversion rate from USD to GBP =1.23. This can be problematic if the conversion rate changes. Consider fetching the conversion rate dynamically


def main():
"""
The main calls all the functions above and communicates with the IFTTT so it can email
any updates about the stocks when triggered.
"""
api_key = "api"
event_name = "stock_update"
url = f"https://maker.ifttt.com/trigger/{event_name}/with/key/{api_key}"

real_time_stock_data = "real_time_data.json"

TICKER_LIST = ["AAPL","TSLA","MSFT","GOOGL","NKE"]
stock_datasets = []
stock_pairs = []
stock_datasets = get_datasets(TICKER_LIST)

for dict in stock_datasets:
stock_pairs.append(generate_stock_queue(dict))
full_data_dict = {k: v for k, v in zip(TICKER_LIST, stock_pairs)}

with open(real_time_stock_data, 'w') as f:
json.dump(full_data_dict, f, indent=4)

for key,value in full_data_dict.items():
print(key, ':', value)
if has_price_decreased(value):
message = f"The stock price for {key} has dropped by at least £0.25GBP. Buy more stocks now!!!"
data = {
'Value1':message
}
response = requests.post(url, json=data)
if response.status_code == 200:
print("Email sent!")
else:
print("Failed to send email.")
print("Yes there has been a drop of prices")
else:
data = {
'Value1':'No updates on the stocks',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

email triggering: If there's no significant decrease, an email is still sent with the message "No updates on the stocks" - I would avoid spamming.

}
response = requests.post(url, json=data)
if response.status_code == 200:
print("Email sent!")
else:
print("Failed to send email.")
print("No decrease")


main()