File size: 6,756 Bytes
2aaf2a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
import streamlit as st
import pandas as pd
import yfinance as yf
import plotly.graph_objects as go
from datetime import datetime, timedelta
import numpy as np

# Import utility functions
from utils.yfinance_utils import fetch_yfinance_daily
from utils.currency_utils import get_usd_sgd_rate
from utils.fd_utils import calculate_fd_returns

# Set page config
st.set_page_config(page_title="Asset Class Comparison", layout="wide")

# Title and description
st.title("Asset Class Performance Comparison")
st.write("Compare the performance of different asset classes over time")

# Sidebar for user inputs
st.sidebar.header("Investment Parameters")
currency = st.sidebar.selectbox("Display Currency", ["USD", "SGD"], index=0)
initial_investment = st.sidebar.number_input(f"Initial Investment Amount ({currency})", min_value=1000, value=10000, step=1000)
start_date = st.sidebar.date_input("Start Date", value=datetime.now() - timedelta(days=365*10))
user_end_date = st.sidebar.date_input("End Date", value=datetime.now())
fd_rate = st.sidebar.number_input("Fixed Deposit Rate (%)", min_value=0.0, value=2.9, step=0.1) / 100

# Asset selection
selected_assets = st.sidebar.multiselect(
    "Select Assets to Compare",
    [
        "Fixed Deposit",
        "Gold",
        "SGS Bonds",
        "US Treasury Bonds",
        "NASDAQ Composite",
        "NASDAQ Large Cap",
        "NASDAQ 100",
        "S&P 500",
        "Dow Jones",
        "Microsoft",
        "Google",
        "Nvidia",
        "Apple",
        "Amazon",
        "Tesla",
        "Netflix",
        "Meta",
    ],
    default=["Fixed Deposit", "Microsoft", "Google", "Nvidia"]
)

# Today's date for reference
today = datetime.now().date()

usd_to_sgd = get_usd_sgd_rate() if currency == "SGD" else 1.0
currency_symbol = "$" if currency == "USD" else "S$"

# Create a dictionary of tickers for yfinance
tickers = {
    "Gold": "GC=F",
    "SGS Bonds": "^TNX",
    "US Treasury Bonds": "^TNX",
    "NASDAQ Composite": "^IXIC",
    "NASDAQ Large Cap": "^NDX",
    "NASDAQ 100": "^NDX",
    "S&P 500": "^GSPC",
    "Dow Jones": "^DJI",
    "Microsoft": "MSFT",
    "Google": "GOOGL",
    "Nvidia": "NVDA",
    "Apple": "AAPL",
    "Amazon": "AMZN",
    "Tesla": "TSLA",
    "Netflix": "NFLX",
    "Meta": "META",
}

# Determine the effective end date for each asset
asset_end_dates = {}
for asset in selected_assets:
    if asset == "Fixed Deposit":
        asset_end_dates[asset] = user_end_date
    else:
        if user_end_date > today:
            asset_end_dates[asset] = today
        else:
            asset_end_dates[asset] = user_end_date

# Warn the user if a future end date is selected for market assets
if any(user_end_date > today and asset != "Fixed Deposit" for asset in selected_assets):
    st.warning(f"Market data is only available up to today ({today}). For market assets, the end date has been set to today.")

# Calculate returns for each selected asset
asset_series = {}
failed_assets = []
actual_start_dates = {}

for asset in selected_assets:
    asset_start = start_date
    asset_end = asset_end_dates[asset]
    if asset == "Fixed Deposit":
        fd_index = pd.date_range(start=asset_start, end=user_end_date)
        daily_rate = (1 + fd_rate) ** (1/365) - 1
        fd_values = initial_investment * (1 + daily_rate) ** np.arange(len(fd_index))
        if currency == "SGD":
            fd_values = fd_values * usd_to_sgd
        asset_series[asset] = pd.Series(fd_values, index=fd_index)
        actual_start_dates[asset] = asset_start
    else:
        price_data = fetch_yfinance_daily(tickers[asset], asset_start, asset_end)
        if price_data is not None and not price_data.empty:
            price_data = price_data.sort_index()
            actual_start = price_data.index[0]
            actual_start_dates[asset] = actual_start
            aligned_index = pd.date_range(start=actual_start, end=asset_end)
            price_data = price_data.reindex(aligned_index)
            price_data = price_data.ffill()
            asset_values = initial_investment * (price_data / price_data.iloc[0])
            if currency == "SGD":
                asset_values = asset_values * usd_to_sgd
            asset_series[asset] = asset_values
        else:
            failed_assets.append(asset)

# Combine all asset series into a single DataFrame
if asset_series:
    returns_data = pd.DataFrame(asset_series)
else:
    returns_data = pd.DataFrame()

# Remove failed assets from selected_assets (except FD)
selected_assets = [asset for asset in selected_assets if asset not in failed_assets or asset == "Fixed Deposit"]

if not selected_assets:
    st.error("No assets could be loaded. Please try different assets.")
    st.stop()

# Create the plot
fig = go.Figure()

for asset in selected_assets:
    fig.add_trace(go.Scatter(
        x=returns_data.index,
        y=returns_data[asset],
        name=asset,
        mode='lines'
    ))

fig.update_layout(
    title="Asset Performance Comparison",
    xaxis_title="Date",
    yaxis_title=f"Investment Value ({currency_symbol})",
    hovermode="x unified",
    height=600
)

# Display the plot
st.plotly_chart(fig, use_container_width=True)

# Calculate and display final returns
st.subheader("Final Investment Values")
for asset in selected_assets:
    valid_series = returns_data[asset].dropna()
    if not valid_series.empty:
        final_value = valid_series.iloc[-1]
        st.write(f"{asset}: {currency_symbol}{final_value:,.2f}")
    else:
        st.write(f"{asset}: Data unavailable")

# Calculate and display annualized returns
st.subheader("Annualized Returns")
for asset in selected_assets:
    valid_series = returns_data[asset].dropna()
    if len(valid_series) > 1:
        actual_start = actual_start_dates[asset]
        days = (valid_series.index[-1] - valid_series.index[0]).days
        years = days / 365
        final_value = valid_series.iloc[-1]
        annualized_return = ((final_value / initial_investment) ** (1/years) - 1) * 100
        if pd.Timestamp(actual_start).date() > start_date:
            st.write(f"{asset}: {annualized_return:.2f}% (Data available from {actual_start.strftime('%Y-%m-%d')})")
        else:
            st.write(f"{asset}: {annualized_return:.2f}%")
    else:
        st.write(f"{asset}: N/A")

# Show warnings for data availability
for asset in selected_assets:
    if asset in actual_start_dates and pd.Timestamp(actual_start_dates[asset]).date() > start_date:
        st.warning(f"Data for {asset} is only available from {actual_start_dates[asset].strftime('%Y-%m-%d')}. The analysis starts from this date.")

# Show warning for failed assets
if failed_assets:
    st.warning(f"Could not load data for the following assets: {', '.join(failed_assets)}")