prasanth.thangavel commited on
Commit
afd8f1d
·
1 Parent(s): b382e22

Made improvements

Browse files
Files changed (3) hide show
  1. app.py +14 -0
  2. requirements.txt +3 -1
  3. utils/hdb_utils.py +137 -0
app.py CHANGED
@@ -9,6 +9,8 @@ import numpy as np
9
  from utils.yfinance_utils import fetch_yfinance_daily
10
  from utils.currency_utils import get_usd_sgd_rate
11
  from utils.fd_utils import calculate_fd_returns
 
 
12
 
13
  print("Starting the app ...")
14
 
@@ -46,6 +48,7 @@ selected_assets = st.sidebar.multiselect(
46
  "Select Assets to Compare",
47
  [
48
  "Fixed Deposit",
 
49
  "Gold",
50
  "SGS Bonds",
51
  "US Treasury Bonds",
@@ -69,6 +72,7 @@ selected_assets = st.sidebar.multiselect(
69
  ],
70
  default=[
71
  "Fixed Deposit",
 
72
  "Gold",
73
  "US Treasury Bonds", "SGS Bonds",
74
  "S&P 500", "Dow Jones", "NASDAQ Composite", #"NASDAQ Large Cap", "NASDAQ 100",
@@ -86,6 +90,7 @@ currency_symbol = "$" if currency == "USD" else "S$"
86
  # Create a dictionary of tickers for yfinance
87
  tickers = {
88
  "Gold": "GC=F",
 
89
  "SGS Bonds": "A35.SI", # Nikko AM SGD Investment Grade Corporate Bond ETF
90
  "US Treasury Bonds": "TLT", # iShares 20+ Year Treasury Bond ETF
91
  "NASDAQ Composite": "^IXIC",
@@ -138,6 +143,15 @@ for asset in selected_assets:
138
  fd_values = fd_values * usd_to_sgd
139
  asset_series[asset] = pd.Series(fd_values, index=fd_index)
140
  actual_start_dates[asset] = asset_start
 
 
 
 
 
 
 
 
 
141
  else:
142
  price_data = fetch_yfinance_daily(tickers[asset], asset_start, asset_end)
143
  if price_data is not None and not price_data.empty:
 
9
  from utils.yfinance_utils import fetch_yfinance_daily
10
  from utils.currency_utils import get_usd_sgd_rate
11
  from utils.fd_utils import calculate_fd_returns
12
+ from utils.hdb_utils import calculate_hdb_returns
13
+
14
 
15
  print("Starting the app ...")
16
 
 
48
  "Select Assets to Compare",
49
  [
50
  "Fixed Deposit",
51
+ "HDB",
52
  "Gold",
53
  "SGS Bonds",
54
  "US Treasury Bonds",
 
72
  ],
73
  default=[
74
  "Fixed Deposit",
75
+ "HDB",
76
  "Gold",
77
  "US Treasury Bonds", "SGS Bonds",
78
  "S&P 500", "Dow Jones", "NASDAQ Composite", #"NASDAQ Large Cap", "NASDAQ 100",
 
90
  # Create a dictionary of tickers for yfinance
91
  tickers = {
92
  "Gold": "GC=F",
93
+ "HDB": "A12.SI",
94
  "SGS Bonds": "A35.SI", # Nikko AM SGD Investment Grade Corporate Bond ETF
95
  "US Treasury Bonds": "TLT", # iShares 20+ Year Treasury Bond ETF
96
  "NASDAQ Composite": "^IXIC",
 
143
  fd_values = fd_values * usd_to_sgd
144
  asset_series[asset] = pd.Series(fd_values, index=fd_index)
145
  actual_start_dates[asset] = asset_start
146
+ elif asset == "HDB":
147
+ hdb_values = calculate_hdb_returns(asset_start, asset_end, initial_investment)
148
+ if hdb_values is not None:
149
+ if currency == "SGD":
150
+ hdb_values = hdb_values * usd_to_sgd
151
+ asset_series[asset] = hdb_values
152
+ actual_start_dates[asset] = asset_start
153
+ else:
154
+ failed_assets.append(asset)
155
  else:
156
  price_data = fetch_yfinance_daily(tickers[asset], asset_start, asset_end)
157
  if price_data is not None and not price_data.empty:
requirements.txt CHANGED
@@ -3,4 +3,6 @@ pandas==2.2.1
3
  yfinance==0.2.56
4
  plotly==5.19.0
5
  python-dotenv==1.0.1
6
- numpy==1.26.4
 
 
 
3
  yfinance==0.2.56
4
  plotly==5.19.0
5
  python-dotenv==1.0.1
6
+ numpy==1.26.4
7
+ requests==2.31.0
8
+ beautifulsoup4==4.12.3
utils/hdb_utils.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ from datetime import datetime
4
+ import requests
5
+ import os
6
+ from functools import lru_cache
7
+ from dotenv import load_dotenv
8
+
9
+ # Load environment variables
10
+ load_dotenv()
11
+
12
+ # SingStat API configuration
13
+ SINGSTAT_API_KEY = os.getenv('SINGSTAT_API_KEY')
14
+ SINGSTAT_API_URL = "https://tablebuilder.singstat.gov.sg/api/table/tabledata"
15
+
16
+ # HDB Resale Price Index table ID
17
+ HDB_TABLE_ID = "M212881"
18
+
19
+ @lru_cache(maxsize=1) # Cache the HDB data for 1 day
20
+ def fetch_hdb_price_index():
21
+ """
22
+ Fetch HDB Resale Price Index from SingStat TableBuilder API
23
+ """
24
+ try:
25
+ if not SINGSTAT_API_KEY:
26
+ raise Exception("SINGSTAT_API_KEY not found in environment variables")
27
+
28
+ # API parameters
29
+ params = {
30
+ "key": SINGSTAT_API_KEY,
31
+ "resourceId": HDB_TABLE_ID,
32
+ "variable": "HDB Resale Price Index",
33
+ "timeFrom": "2000",
34
+ "timeTo": datetime.now().strftime("%Y")
35
+ }
36
+
37
+ # Make API request
38
+ response = requests.get(SINGSTAT_API_URL, params=params)
39
+ response.raise_for_status()
40
+
41
+ # Parse response
42
+ data = response.json()
43
+ if 'Data' not in data:
44
+ raise Exception("Invalid response format from SingStat API")
45
+
46
+ # Extract the data
47
+ records = data['Data']
48
+ if not records:
49
+ raise Exception("No records found in SingStat API response")
50
+
51
+ # Convert to DataFrame
52
+ df = pd.DataFrame(records)
53
+
54
+ # Process the data
55
+ # The exact column names might need adjustment based on the actual API response
56
+ df['year'] = pd.to_datetime(df['year'], format='%Y')
57
+ df['value'] = pd.to_numeric(df['value'])
58
+
59
+ # Create a dictionary of year to index
60
+ yearly_data = df.set_index('year')['value']
61
+
62
+ # Convert to dictionary with string keys
63
+ result = {str(year.year): float(value) for year, value in yearly_data.items()}
64
+ return result
65
+
66
+ except Exception as e:
67
+ print(f"Error fetching HDB data from SingStat: {e}")
68
+ # Fallback to hardcoded data if API fails
69
+ return {
70
+ '2000': 78.3,
71
+ '2001': 75.6,
72
+ '2002': 74.1,
73
+ '2003': 73.1,
74
+ '2004': 74.3,
75
+ '2005': 80.0,
76
+ '2006': 88.0,
77
+ '2007': 100.0,
78
+ '2008': 110.0,
79
+ '2009': 100.0, # Base year
80
+ '2010': 105.0,
81
+ '2011': 111.0,
82
+ '2012': 118.0,
83
+ '2013': 123.0,
84
+ '2014': 120.0,
85
+ '2015': 115.0,
86
+ '2016': 110.0,
87
+ '2017': 108.0,
88
+ '2018': 110.0,
89
+ '2019': 111.0,
90
+ '2020': 112.0,
91
+ '2021': 120.0,
92
+ '2022': 130.0,
93
+ '2023': 140.0,
94
+ '2024': 145.0
95
+ }
96
+
97
+ def calculate_hdb_returns(start_date, end_date, initial_investment):
98
+ """
99
+ Calculate HDB price returns based on the HDB Resale Price Index
100
+ """
101
+ try:
102
+ # Get the latest HDB price index data
103
+ hdb_index = fetch_hdb_price_index()
104
+
105
+ # Create a date range for the investment period
106
+ date_range = pd.date_range(start=start_date, end=end_date)
107
+
108
+ # Convert the yearly data to a Series with datetime index
109
+ yearly_dates = pd.to_datetime([f"{year}-01-01" for year in hdb_index.keys()])
110
+ yearly_values = list(hdb_index.values())
111
+ yearly_prices = pd.Series(yearly_values, index=yearly_dates)
112
+
113
+ # Sort by date to ensure proper interpolation
114
+ yearly_prices = yearly_prices.sort_index()
115
+
116
+ # Interpolate the price index for each day
117
+ daily_prices = yearly_prices.reindex(date_range, method='ffill')
118
+
119
+ # Calculate the return based on the price index
120
+ if not daily_prices.empty:
121
+ start_price = daily_prices.iloc[0]
122
+ daily_returns = daily_prices / start_price
123
+ investment_value = initial_investment * daily_returns
124
+ return investment_value
125
+
126
+ return None
127
+
128
+ except Exception as e:
129
+ print(f"Error calculating HDB returns: {e}")
130
+ return None
131
+
132
+ if __name__ == "__main__":
133
+ # Test the function
134
+ start = datetime(2000, 1, 1)
135
+ end = datetime(2024, 1, 1)
136
+ result = calculate_hdb_returns(start, end, 100000)
137
+ print(result)