InvestSim / app.py
0xrushi's picture
Create app.py
fcd46c3 verified
import streamlit as st
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
def fetch_stock_data(symbol, start_date, end_date):
return yf.download(symbol, start=start_date, end=end_date)
def backtest_mixed_investment(data, start_date, end_date, monthly_investment, stock_allocation, savings_rate):
investment_dates = []
stock_shares = 0
savings_balance = 0
total_invested = 0
stock_value = []
savings_value = []
stock_investment = monthly_investment * stock_allocation
savings_investment = monthly_investment * (1 - stock_allocation)
daily_rate = (1 + savings_rate) ** (1/365) - 1
current_date = pd.to_datetime(start_date)
prev_date = current_date
while current_date <= pd.to_datetime(end_date):
if current_date in data.index:
price = data.loc[current_date, 'Adj Close']
new_shares = stock_investment / price
stock_shares += new_shares
days_passed = (current_date - prev_date).days
savings_balance *= (1 + daily_rate) ** days_passed
savings_balance += savings_investment
total_invested += monthly_investment
investment_dates.append(current_date)
stock_value.append(stock_shares * price)
savings_value.append(savings_balance)
prev_date = current_date
current_date += pd.DateOffset(months=1)
stock_value_series = pd.Series(stock_value, index=investment_dates).reindex(data.index, method='ffill')
savings_value_series = pd.Series(savings_value, index=investment_dates).reindex(data.index, method='ffill')
portfolio_value = stock_value_series + savings_value_series
return portfolio_value, stock_value_series, savings_value_series, total_invested
def backtest_stock_only(data, start_date, end_date, monthly_investment):
investment_dates = []
total_shares = 0
total_invested = 0
current_date = pd.to_datetime(start_date)
while current_date <= pd.to_datetime(end_date):
if current_date in data.index:
price = data.loc[current_date, 'Adj Close']
shares = monthly_investment / price
total_shares += shares
total_invested += monthly_investment
investment_dates.append(current_date)
current_date += pd.DateOffset(months=1)
total_shares_series = pd.Series([total_shares] * len(investment_dates), index=investment_dates).reindex(data.index, method='ffill')
portfolio_value = data['Adj Close'] * total_shares_series
return portfolio_value, pd.Series([total_invested] * len(data.index), index=data.index), total_invested
def plot_results(portfolio_value, stock_value, savings_value, total_invested, symbol, start_date, end_date, stock_only=False):
fig, ax = plt.subplots(figsize=(12, 6))
ax.plot(portfolio_value.index, portfolio_value, label='Portfolio Value')
if not stock_only:
ax.plot(stock_value.index, stock_value, label='Stock Investment Value')
ax.plot(savings_value.index, savings_value, label='Savings Account Value')
ax.plot(portfolio_value.index, total_invested, label='Total Cash Invested', linestyle='--')
ax.set_title(f'Monthly Investment Analysis: {symbol} ({start_date} to {end_date})')
ax.set_xlabel('Date')
ax.set_ylabel('Value ($)')
ax.legend()
ax.grid(True)
total_return = (portfolio_value[-1] - total_invested[-1]) / total_invested[-1] * 100
ax.annotate(f'Total Return: {total_return:.2f}%', xy=(0.05, 0.95), xycoords='axes fraction', fontsize=10, ha='left', va='top')
ax.annotate(f'Final Portfolio Value: ${portfolio_value[-1]:.2f}', xy=(0.05, 0.90), xycoords='axes fraction', fontsize=10, ha='left', va='top')
if not stock_only:
ax.annotate(f'Final Stock Value: ${stock_value[-1]:.2f}', xy=(0.05, 0.85), xycoords='axes fraction', fontsize=10, ha='left', va='top')
ax.annotate(f'Final Savings Value: ${savings_value[-1]:.2f}', xy=(0.05, 0.80), xycoords='axes fraction', fontsize=10, ha='left', va='top')
ax.annotate(f'Total Invested: ${total_invested[-1]:.2f}', xy=(0.05, 0.75), xycoords='axes fraction', fontsize=10, ha='left', va='top')
return fig
def main():
st.title("InvestSim: Stock & Savings Portfolio Analyzer")
st.write("Simulate and analyze your investment strategy with stocks and high-yield savings accounts.")
# Sidebar inputs
st.sidebar.header('Input Parameters')
symbol = st.sidebar.text_input('Stock Symbol', 'AAPL')
start_date = st.sidebar.date_input('Start Date', datetime(2000, 1, 1))
end_date = st.sidebar.date_input('End Date', datetime.now())
monthly_investment = st.sidebar.number_input('Monthly Investment ($)', min_value=1, value=100)
investment_type = st.sidebar.radio("Investment Type", ("Stock Only", "Mixed (Stock + Savings)"))
if investment_type == "Mixed (Stock + Savings)":
stock_allocation = st.sidebar.slider('Stock Allocation (%)', 0, 100, 60) / 100
savings_rate = st.sidebar.number_input('HYSA Annual Interest Rate (%)', min_value=0.0, max_value=20.0, value=4.5) / 100
else:
stock_allocation = 1.0
savings_rate = 0.0
if st.sidebar.button('Run Analysis'):
# Fetch stock data
data = fetch_stock_data(symbol, start_date, end_date)
if data.empty:
st.error(f"No data available for {symbol}. Please check the stock symbol and date range.")
return
# Run backtest
if investment_type == "Mixed (Stock + Savings)":
portfolio_value, stock_value, savings_value, total_invested = backtest_mixed_investment(
data, start_date, end_date, monthly_investment, stock_allocation, savings_rate
)
else:
portfolio_value, total_invested_series, total_invested = backtest_stock_only(
data, start_date, end_date, monthly_investment
)
stock_value = portfolio_value
savings_value = pd.Series([0] * len(portfolio_value), index=portfolio_value.index)
# Plot results
fig = plot_results(portfolio_value, stock_value, savings_value, total_invested_series if investment_type == "Stock Only" else pd.Series([total_invested] * len(portfolio_value), index=portfolio_value.index), symbol, start_date, end_date, stock_only=(investment_type == "Stock Only"))
st.pyplot(fig)
# Display summary statistics
st.subheader('Investment Summary')
st.write(f"Total Return: {((portfolio_value[-1] - total_invested) / total_invested * 100):.2f}%")
st.write(f"Final Portfolio Value: ${portfolio_value[-1]:.2f}")
if investment_type == "Mixed (Stock + Savings)":
st.write(f"Final Stock Value: ${stock_value[-1]:.2f}")
st.write(f"Final Savings Value: ${savings_value[-1]:.2f}")
st.write(f"Total Invested: ${total_invested:.2f}")
if __name__ == '__main__':
main()