-
Notifications
You must be signed in to change notification settings - Fork 16
pull request #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
pull request #17
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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]} | ||
| 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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.") | ||
|
|
||
|
|
||
| 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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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', | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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() | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
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?