-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 00c44d0
Showing
21 changed files
with
1,139 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"folders": [ | ||
{ | ||
"uri": "vscode-remote://codespaces+cuddly-space-doodle-rxrj99x7p6vf5v5w/workspaces/FinRisk" | ||
} | ||
], | ||
"remoteAuthority": "codespaces+cuddly-space-doodle-rxrj99x7p6vf5v5w", | ||
"settings": {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2024 Samar Patel | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
![cover_photo](./readmefile/Cover.png) | ||
|
||
# MindInventory - Portfolio & Risk Management System | ||
*FinRisk allows you to build a custom portfolio and assess its risk within | ||
few clicks!* | ||
|
||
*Portfolio Risk management is like wearing a helmet while riding a bike—it | ||
shields your money during investments. For average investors, it's hard to | ||
understand the risk of their portfolio, FinRisk is here to help.* | ||
|
||
## Core Features | ||
### Real Time Market Condition Previewer | ||
Quick Glimpse of the Financial Frenzy! The market preview includes current | ||
market status of Dow Jones, S&P 500, Nasdaq, and Russell 2000 index. Hold onto | ||
your seat as we zoom into the exciting world of the top 8 tech and meme stocks! | ||
🚀🎢 Get the scoop on current prices, price changes, and the percentage shake-ups. | ||
![market_preview](./readmefile/market_preview.png) | ||
### Portfolio Builder | ||
Dream big and curate a portfolio that reflects your financial aspirations. You | ||
have the power to choose up to 10 stocks!For each stock in your portfolio, some | ||
fundamental information required: stock tickers, the specific quantity of shares, | ||
and the record of purchase dates. | ||
![portfolio_builder](./readmefile/portfolio_builder.png) | ||
|
||
### Portfolio Risk Simulator | ||
The portfolio risk simulator is a powerful tool that allows you to assess your portfolio's risk using | ||
Monte Carlo Simulation. The simulation unravels crucial metrics: Value at Risk (VaR) and Conditional Value | ||
at Risk (CVaR). VaR adn CVaR are two of the most popular risk metrics used by financial institutions to | ||
measure the risk of their portfolios. Simulation can be tailored to your needs by adjusting the number | ||
of simulations, historical data of your selected stocks, the confidence levels (alpha). The simulation | ||
will also generate a line chart for your portfolio's daily returns. All data can be downloaded as a CSV file for | ||
further analysis. | ||
![portfolio_risk_simulator](./readmefile/risk_model.png) | ||
## How to Use | ||
👉 click [MRMST](https://mrmst.streamlit.app/) to launch the app! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import yfinance | ||
import pandas as pd | ||
|
||
|
||
class InfoCollector: | ||
|
||
@staticmethod | ||
def get_ticker(stock_name: str) -> yfinance.Ticker: | ||
return yfinance.Ticker(stock_name) | ||
|
||
@staticmethod | ||
def get_history(ticker: yfinance.Ticker, | ||
period="1mo", interval="1d", | ||
start=None, end=None): | ||
""" | ||
period : str | ||
Valid periods: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max | ||
Either Use period parameter or use start and end | ||
interval : str | ||
Valid intervals: 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo | ||
Intraday data cannot extend last 60 days | ||
start: str | ||
Download start date string (YYYY-MM-DD) or _datetime, inclusive. | ||
Default is 99 years ago | ||
E.g. for start="2020-01-01", the first data point will be on "2020-01-01" | ||
end: str | ||
Download end date string (YYYY-MM-DD) or _datetime, exclusive. | ||
Default is now | ||
E.g. for end="2023-01-01", the last data point will be on "2022-12-31" | ||
""" | ||
return ticker.history(period=period, interval=interval, | ||
start=start, end=end) | ||
|
||
@staticmethod | ||
def get_demo_daily_history(interval: str): | ||
return InfoCollector.get_history( | ||
ticker=yfinance.Ticker("AAPL"), | ||
period="1d", | ||
interval=interval, | ||
start="2023-11-15", | ||
end="2023-11-16") | ||
|
||
@staticmethod | ||
def get_prev_date(stock_info: pd.DataFrame): | ||
return stock_info.index[0] | ||
|
||
@staticmethod | ||
def get_daily_info(stock_info: pd.DataFrame, key_info: str): | ||
return stock_info.loc[stock_info.index[0], key_info] | ||
|
||
@staticmethod | ||
def download_batch_history(stocks: list, start_time, end_time): | ||
return yfinance.download(stocks, start=start_time, end=end_time) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
from assets.Stock import Stock | ||
|
||
|
||
class Portfolio: | ||
|
||
def __init__(self) -> None: | ||
self.stocks = {} | ||
self.book_amount = 0 | ||
self.market_value = 0 | ||
|
||
def add_stock(self, stock: Stock) -> None: | ||
if stock.stock_name in self.stocks.keys(): | ||
raise Exception("Stock included in portfolio. Please remove stock to add again") | ||
|
||
self.stocks[stock.stock_name] = stock | ||
self.book_amount += stock.get_book_cost() | ||
|
||
def remove_stock(self, stock_name: str) -> None: | ||
if stock_name not in self.stocks.keys(): | ||
raise Exception("Stock not in portfolio") | ||
self.book_amount -= self.stocks[stock_name].get_book_cost() | ||
self.stocks.pop(stock_name) | ||
|
||
def update_market_value(self) -> None: | ||
for stock in self.stocks.values(): | ||
stock_market_value = stock.get_market_value() | ||
self.market_value += stock_market_value |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import datetime | ||
from assets.Collector import InfoCollector | ||
|
||
|
||
class Stock: | ||
|
||
def __init__(self, stock_name: str): | ||
self.stock_name = stock_name | ||
self.ticker = InfoCollector.get_ticker(stock_name) | ||
self.owned_quantity = 0 | ||
self.average_price = 0 | ||
self.previous_close = None | ||
self.previous_open = None | ||
self.previous_volume = None | ||
self.previous_date = None | ||
|
||
self._update_stock() | ||
|
||
def __eq__(self, other): | ||
if self.stock_name == other.stock_name: | ||
return True | ||
return False | ||
|
||
def _update_stock(self) -> None: | ||
""" | ||
Updates the stock information, used as a check function to check if | ||
stock exist | ||
""" | ||
stock_info = InfoCollector.get_history(self.ticker, period="1d") | ||
if len(stock_info) == 0: | ||
raise Exception("Invalid stock, enter a valid stock") | ||
else: | ||
self.previous_date = InfoCollector.get_prev_date(stock_info) | ||
self.previous_open = InfoCollector.get_daily_info(stock_info, "Open") | ||
self.previous_close = InfoCollector.get_daily_info(stock_info, "Close") | ||
self.previous_volume = InfoCollector.get_daily_info(stock_info, "Volume") | ||
|
||
def _get_purchase_price(self, purchase_date: datetime.datetime) -> float: | ||
""" | ||
Gets the purchase price (assumed be closed price) of the stock based | ||
on given date if price at given date not found, track back for 5 days, | ||
thought: smart implementation might be required for caching | ||
""" | ||
time_delta = datetime.timedelta(days=1) | ||
start_date = purchase_date | ||
end_date = purchase_date + time_delta | ||
|
||
for _ in range(5): | ||
info = InfoCollector.get_history(self.ticker, | ||
start=start_date, | ||
end=end_date) | ||
if len(info) > 0: | ||
purchased_price = InfoCollector.get_daily_info(info, "Close") | ||
return purchased_price | ||
start_date = start_date - time_delta | ||
end_date = end_date - time_delta | ||
|
||
raise Exception("Purchase price not found, please check the date or stock sticker") | ||
|
||
def add_buy_action(self, quantity: int, | ||
purchase_date: datetime.datetime) -> None: | ||
""" | ||
Add a purchase to the stock. Currently, do not support to add another | ||
purchase to the stock | ||
""" | ||
|
||
# update own quantity | ||
|
||
self.owned_quantity += quantity | ||
|
||
if self.average_price == 0: | ||
|
||
self.average_price = self._get_purchase_price(purchase_date=purchase_date) | ||
else: | ||
cur_purchase_price = self._get_purchase_price(purchase_date=purchase_date) | ||
purchase_cost = quantity * cur_purchase_price | ||
|
||
# update average price and owned_quantity | ||
total_cost = purchase_cost + cur_purchase_price | ||
self.average_price = total_cost / self.owned_quantity | ||
|
||
def get_book_cost(self) -> float: | ||
if self.owned_quantity == 0: | ||
raise Exception("Stock not owned, please purchase first") | ||
|
||
if self.average_price is None: | ||
raise Exception("Purchase price not found, please check the date or stock sticker") | ||
|
||
return self.average_price * self.owned_quantity | ||
|
||
def get_market_value(self) -> float: | ||
self._update_stock() | ||
if self.owned_quantity == 0: | ||
raise Exception("Stock not owned, please purchase first") | ||
|
||
if self.previous_close is None: | ||
raise Exception("Stock price not found, please check the date or stock sticker") | ||
|
||
return self.previous_close * self.owned_quantity | ||
|
||
def get_gain_loss(self) -> float: | ||
return self.get_market_value() - self.get_book_cost() | ||
|
||
def get_pct_change(self) -> float: | ||
return (self.get_gain_loss() / self.get_book_cost()) * 100 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import streamlit as st | ||
import stTools as tools | ||
|
||
|
||
def load_page(): | ||
st.markdown( | ||
""" | ||
Welcome to :green[MRMST]! Explore this app to assess and simulate | ||
your investment portfolio's risk effortlessly! :green[Risk management] is like | ||
:blue[wearing a helmet while riding a bike]—it shields your money during investments. | ||
It's a strategy set to understand uncertainties in stocks or bonds. | ||
Imagine your investment journey as a game; knowing rules and setbacks gives you a competitive edge. | ||
:green[Value at Risk (VaR)] and :green[Conditional Value at Risk (CVaR)] aid in smart risk navigation, | ||
keeping your game plan robust. Don't worry, I'll explain these concepts in a bit. | ||
Build your :green[portfolio] on the sidebar; guidance is provided! Contact me at | ||
[LinkedIn](https://www.linkedin.com/in/samarpatel/) | ||
""" | ||
) | ||
|
||
st.subheader(f"Market Preview") | ||
|
||
col_stock1, col_stock_2, col_stock_3, col_stock_4 = st.columns(4) | ||
|
||
with col_stock1: | ||
tools.create_candle_stick_plot(stock_ticker_name="^DJI", | ||
stock_name="Dow Jones Industrial") | ||
|
||
with col_stock_2: | ||
tools.create_candle_stick_plot(stock_ticker_name="^IXIC", | ||
stock_name="Nasdaq Composite") | ||
|
||
with col_stock_3: | ||
tools.create_candle_stick_plot(stock_ticker_name="^GSPC", | ||
stock_name="S&P 500") | ||
|
||
with col_stock_4: | ||
tools.create_candle_stick_plot(stock_ticker_name="^RUT", | ||
stock_name="Russell 2000") | ||
|
||
# make 2 columns for sectors | ||
col_sector1, col_sector2 = st.columns(2) | ||
|
||
with col_sector1: | ||
st.subheader(f"Tech Stocks") | ||
stock_list = ["AAPL", "MSFT", "AMZN", "GOOG", "META", "TSLA", "NVDA", "AVGO"] | ||
stock_name = ["Apple", "Microsoft", "Amazon", "Google", "Meta", "Tesla", "Nvidia", "Broadcom"] | ||
|
||
df_stocks = tools.create_stocks_dataframe(stock_list, stock_name) | ||
tools.create_dateframe_view(df_stocks) | ||
|
||
with col_sector2: | ||
st.subheader(f"Meme Stocks") | ||
# give me a list of 8 meme stocks | ||
stock_list = ["GME", "AMC", "BB", "NOK", "RIVN", "SPCE", "F", "T"] | ||
stock_name = ["GameStop", "AMC Entertainment", "BlackBerry", "Nokia", "Rivian", | ||
"Virgin Galactic", "Ford", "AT&T"] | ||
|
||
df_stocks = tools.create_stocks_dataframe(stock_list, stock_name) | ||
tools.create_dateframe_view(df_stocks) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import streamlit as st | ||
import side_bar as comp | ||
import stTools as tools | ||
import default_page | ||
import portfolio_page | ||
import model_page | ||
|
||
st.set_page_config( | ||
page_title="MindInventory Financial Risk Tool", | ||
page_icon="🦈", | ||
layout="wide" | ||
) | ||
|
||
tools.remove_white_space() | ||
|
||
st.title("MindInventory Risk Management Simulation Tool") | ||
|
||
comp.load_sidebar() | ||
|
||
if "load_portfolio_check" not in st.session_state: | ||
st.session_state["load_portfolio_check"] = False | ||
|
||
if "run_simulation_check" not in st.session_state: | ||
st.session_state["run_simulation_check"] = False | ||
|
||
if not st.session_state.load_portfolio_check: | ||
default_page.load_page() | ||
|
||
elif not st.session_state.run_simulation_check and st.session_state.load_portfolio_check: | ||
portfolio_page.load_page() | ||
|
||
elif st.session_state.run_simulation_check: | ||
model_page.load_page() |
Oops, something went wrong.