From 38f94ce478fd79a428123ca181e15da4b7cf4a7a Mon Sep 17 00:00:00 2001 From: psyes9 Date: Mon, 4 Dec 2023 16:58:34 +0000 Subject: [PATCH] setting up API communication for stock graphing --- .../current_stock_holdings.cpython-38.pyc | Bin 0 -> 2228 bytes backend/myapp/__pycache__/urls.cpython-38.pyc | Bin 335 -> 453 bytes .../myapp/__pycache__/views.cpython-38.pyc | Bin 2507 -> 1989 bytes backend/myapp/current_stock_holdings.py | 75 ++++++++++++++++ backend/myapp/graph_stock_holdings.py | 63 ++++++++++++++ backend/myapp/urls.py | 6 +- backend/myapp/views.py | 81 +----------------- 7 files changed, 146 insertions(+), 79 deletions(-) create mode 100644 backend/myapp/__pycache__/current_stock_holdings.cpython-38.pyc create mode 100644 backend/myapp/current_stock_holdings.py create mode 100644 backend/myapp/graph_stock_holdings.py diff --git a/backend/myapp/__pycache__/current_stock_holdings.cpython-38.pyc b/backend/myapp/__pycache__/current_stock_holdings.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b8c0bcfda42d163e22875d51d5ebe854f92303f GIT binary patch literal 2228 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 literal 2507 zcmb7G&u<*H7q>mLGqXRlyV-1#7Rn-3NL!(V3m~B=qBI445sFHaKp9c3CTs8RbZ2L_ zwv&>T=X*)7e5r&u0aB{(iT{GXf`hMIC|5412gI{C%@2@}n2|lt&(F{H$Dj8+d(~3< z23E#NF)UG!`j1gqrU5O&w?gp_>Xx2?XSChLy7HmO(z{Gdvs5r>2ZBi%r$Yg6Z&_em5YEU#TKEZrb1**|P;05I^Svk{xa;~S1W0EgHU+Z+tOtsYF3Q~(e z;pTj-wP^z`b+8NQ;xSPTwWQj#^_{26X7`_>hd;VVR>vin^;eAYWsGJwWQ{H<|64@c zYWZ9Ay?cx?QodYJ%Mc>V-;oz%--K|^m;bwb4}AuH?m>}tRW>-%6}tKu(JS=omrlMy zU$O|VSai#>p~}Z5lwaPq{N+CIg&SJUs=F~zk#}~ECi_`3t8Qm&oovwA8AY6lS$+4M zi=Ex0DVtrndB}J)WSv`?ke#U+X-qqNJ6l8WXCLjnxA)m4j+)prLAV1&T!Sh`3f*tR zDdU_dzfu5>;DMtsrhb~XlKlhOhN_D2zF_aml)9or$g)xwAP-N3CP#zNo5z55;}qb%ZjE?$ zkWyW|{Y9TmO(xa_IJi`5M@X(c0sLU6H^wTj8Q%-0GH8d5iwWG`Nn!AUfnh6&BwQ}N zXdXtrK7^6b?15062kQb z> 0} + + # Fetch live data for remaining stocks + live_data = {} + for ticker, shares in holdings.items(): + try: + stock = yf.Ticker(ticker) + stock_info = stock.info + + # Determine the correct fields based on the ticker + name_field = "longName" if ticker == "VUAG.L" else "shortName" + price_field = "previousClose" if ticker == "VUAG.L" else "currentPrice" + + # Fetch the relevant data + name = stock_info.get(name_field) + current_price = stock_info.get(price_field) or stock_info.get("previousClose") + average_cost = latest_average_costs[ticker] + total_investment = average_cost * shares + current_value = shares * current_price if current_price is not None else None + value_held = round((shares * current_price if current_price is not None else None),2) + + profit_loss_percentage = round((((current_value - total_investment) / total_investment * 100) if current_value is not None else None),2) + + + live_data[ticker] = { + "ticker": ticker, + "name": name, + "shares_held": round(shares,4), + "current_price": current_price, + "value_held": value_held, + "profit_loss_percentage": profit_loss_percentage + } + except Exception as e: + print(f"Error fetching data for {ticker}: {e}") + live_data[ticker] = { + "ticker": ticker, + "error": str(e) + } + + sorting_data = sorted(live_data.items(), key=lambda x: x[1].get('value_held', 0), reverse=True) + live_data = {k: v for k, v in sorting_data} + + return JsonResponse(live_data) \ No newline at end of file diff --git a/backend/myapp/graph_stock_holdings.py b/backend/myapp/graph_stock_holdings.py new file mode 100644 index 0000000..42b28f3 --- /dev/null +++ b/backend/myapp/graph_stock_holdings.py @@ -0,0 +1,63 @@ +import json +from django.http import JsonResponse +from django.conf import settings +from pathlib import Path +import yfinance as yf +import pytz +from datetime import datetime + +# Your timezone, for example, 'UTC' +timezone = pytz.timezone('EST') + +def get_stock_history(request, ticker): + if not ticker: + return JsonResponse({"error": "Ticker symbol is required."}, status=400) + + # Define the path to the JSON file + json_file_path = Path(settings.BASE_DIR) / 'data' / 'investments_data.json' + + # Open the JSON file and load its content + with open(json_file_path, 'r') as file: + transactions_data = json.load(file) + + transactions = [t for t in transactions_data if t['Ticker Symbol'] == ticker] + + if not transactions: + return JsonResponse({"error": "No transactions found for given ticker."}, status=404) + + # Sort transactions by date + transactions.sort(key=lambda x: datetime.strptime(x["Date"], '%d-%m-%Y')) + + # Get the first purchase date and last sale/current date + start_date = datetime.strptime(transactions[0]["Date"], '%d-%m-%Y') + end_date = datetime.now() + + stock = yf.Ticker(ticker) + historical_prices = stock.history(start=start_date.strftime('%Y-%m-%d'), end=end_date.strftime('%Y-%m-%d')) + + historical_values = [] + current_shares = 0 + + # Before the loop, sort transactions by date + transactions.sort(key=lambda x: datetime.strptime(x["Date"], '%d-%m-%Y')) + + for date, row in historical_prices.iterrows(): + date = date.to_pydatetime().replace(tzinfo=None) # Make date timezone naive + current_shares = 0 # Reset current shares for each date + + # Accumulate shares up to the current date + for transaction in transactions: + transaction_date = datetime.strptime(transaction["Date"], '%d-%m-%Y') + + if transaction_date <= date: + shares = float(transaction["No. of Shares"]) + if transaction["Transaction Type"] == "BUY": + current_shares += shares + elif transaction["Transaction Type"] == "SELL": + current_shares -= shares + + # Calculate value for the current date + value = current_shares * row['Close'] + historical_values.append({"date": date.strftime('%Y-%m-%d'), "value": value}) + + return JsonResponse(historical_values, safe=False) diff --git a/backend/myapp/urls.py b/backend/myapp/urls.py index 47c9f9c..a5074cf 100644 --- a/backend/myapp/urls.py +++ b/backend/myapp/urls.py @@ -1,6 +1,8 @@ from django.urls import path -from .views import get_stock_holdings +from .current_stock_holdings import get_stock_holdings +from .graph_stock_holdings import get_stock_history urlpatterns = [ path('stock_holdings/', get_stock_holdings, name='stock_holdings'), -] + path('graph_stock//', get_stock_history, name='graph_stock'), +] \ No newline at end of file diff --git a/backend/myapp/views.py b/backend/myapp/views.py index 73f81ce..6734cc9 100644 --- a/backend/myapp/views.py +++ b/backend/myapp/views.py @@ -2,83 +2,10 @@ from django.http import JsonResponse from django.conf import settings from pathlib import Path -from rest_framework import viewsets import yfinance as yf +import pytz +from datetime import datetime +# Your timezone, for example, 'UTC' +timezone = pytz.timezone('EST') -def load_transactions(request): - json_file_path = Path(settings.BASE_DIR) / 'data' / 'investments_data.json' - with open(json_file_path, 'r') as file: - data = json.load(file) - return JsonResponse(data, safe=False) - -def get_stock_holdings(request): - # Define the path to the JSON file - json_file_path = Path(settings.BASE_DIR) / 'data' / 'investments_data.json' - - - # Open the JSON file and load its content - with open(json_file_path, 'r') as file: - transactions_data = json.load(file) - - # Process transactions to determine current holdings - holdings = {} - latest_average_costs = {} - for transaction in transactions_data: - ticker = transaction["Ticker Symbol"] - shares = float(transaction["No. of Shares"]) - transaction_type = transaction["Transaction Type"] - avg_cost = float(transaction["Average Cost per Share USD"]) - - - if ticker not in holdings: - holdings[ticker] = 0.0 - - holdings[ticker] += shares if transaction_type == "BUY" else -shares - latest_average_costs[ticker] = avg_cost # Update with the latest average cost - - - # Filter out stocks where holdings are zero or negative - holdings = {k: v for k, v in holdings.items() if v > 0} - - # Fetch live data for remaining stocks - live_data = {} - for ticker, shares in holdings.items(): - try: - stock = yf.Ticker(ticker) - stock_info = stock.info - - # Determine the correct fields based on the ticker - name_field = "longName" if ticker == "VUAG.L" else "shortName" - price_field = "previousClose" if ticker == "VUAG.L" else "currentPrice" - - # Fetch the relevant data - name = stock_info.get(name_field) - current_price = stock_info.get(price_field) or stock_info.get("previousClose") - average_cost = latest_average_costs[ticker] - total_investment = average_cost * shares - current_value = shares * current_price if current_price is not None else None - value_held = round((shares * current_price if current_price is not None else None),2) - - profit_loss_percentage = round((((current_value - total_investment) / total_investment * 100) if current_value is not None else None),2) - - - live_data[ticker] = { - "ticker": ticker, - "name": name, - "shares_held": round(shares,4), - "current_price": current_price, - "value_held": value_held, - "profit_loss_percentage": profit_loss_percentage - } - except Exception as e: - print(f"Error fetching data for {ticker}: {e}") - live_data[ticker] = { - "ticker": ticker, - "error": str(e) - } - - sorting_data = sorted(live_data.items(), key=lambda x: x[1].get('value_held', 0), reverse=True) - live_data = {k: v for k, v in sorting_data} - - return JsonResponse(live_data) \ No newline at end of file