eagle0504 commited on
Commit
da48dbc
1 Parent(s): cad109e

test new feature

Browse files
Files changed (2) hide show
  1. utils/helper.py +208 -0
  2. utils/ui_helper.py +9 -0
utils/helper.py CHANGED
@@ -1,5 +1,7 @@
1
  # helper.py
2
  import yfinance as yf
 
 
3
  import pandas as pd
4
  from typing import List, Tuple, Dict
5
 
@@ -23,6 +25,31 @@ def download_stock_data(
23
  return data["Adj Close"]
24
 
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  def create_portfolio_and_calculate_returns(
27
  stock_data: pd.DataFrame, top_n: int
28
  ) -> pd.DataFrame:
@@ -75,3 +102,184 @@ def create_portfolio_and_calculate_returns(
75
  ).cumprod()
76
 
77
  return new_returns_data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # helper.py
2
  import yfinance as yf
3
+ import matplotlib.pyplot as plt
4
+ import numpy as np
5
  import pandas as pd
6
  from typing import List, Tuple, Dict
7
 
 
25
  return data["Adj Close"]
26
 
27
 
28
+ def download_stocks(tickers: List[str]) -> List[pd.DataFrame]:
29
+ """
30
+ Downloads stock data from Yahoo Finance.
31
+
32
+ Args:
33
+ tickers: A list of stock tickers.
34
+
35
+ Returns:
36
+ A list of Pandas DataFrames, one for each stock.
37
+ """
38
+
39
+ # Create a list of DataFrames.
40
+ df_list = []
41
+
42
+ # Iterate over the tickers.
43
+ for ticker in tickers:
44
+ # Download the stock data.
45
+ df = yf.download(ticker)
46
+
47
+ # Add the DataFrame to the list.
48
+ df_list.append(df.tail(255 * 8))
49
+
50
+ return df_list
51
+
52
+
53
  def create_portfolio_and_calculate_returns(
54
  stock_data: pd.DataFrame, top_n: int
55
  ) -> pd.DataFrame:
 
102
  ).cumprod()
103
 
104
  return new_returns_data
105
+
106
+
107
+ def portfolio_annualised_performance(
108
+ weights: np.ndarray, mean_returns: np.ndarray, cov_matrix: np.ndarray
109
+ ) -> Tuple[float, float]:
110
+ """
111
+ Given the weights of the assets in the portfolio, their mean returns, and their covariance matrix,
112
+ this function computes and returns the annualized performance of the portfolio in terms of its
113
+ standard deviation (volatility) and expected returns.
114
+
115
+ Args:
116
+ weights (np.ndarray): The weights of the assets in the portfolio.
117
+ Each weight corresponds to the proportion of the investor's total
118
+ investment in the corresponding asset.
119
+
120
+ mean_returns (np.ndarray): The mean (expected) returns of the assets.
121
+
122
+ cov_matrix (np.ndarray): The covariance matrix of the asset returns. Each entry at the
123
+ intersection of a row and a column represents the covariance
124
+ between the returns of the asset corresponding to that row
125
+ and the asset corresponding to that column.
126
+
127
+ Returns:
128
+ Tuple of portfolio volatility (standard deviation) and portfolio expected return, both annualized.
129
+ """
130
+
131
+ # Annualize portfolio returns by summing up the products of the mean returns and weights of each asset and then multiplying by 252
132
+ # (number of trading days in a year)
133
+ returns = np.sum(mean_returns * weights) * 252
134
+
135
+ # Compute portfolio volatility (standard deviation) by dot multiplying the weights transpose and the dot product of covariance matrix
136
+ # and weights. Then take the square root to get the standard deviation and multiply by square root of 252 to annualize it.
137
+ std = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights))) * np.sqrt(252)
138
+
139
+ return std, returns
140
+
141
+
142
+ def random_portfolios(
143
+ num_portfolios: int,
144
+ num_weights: int,
145
+ mean_returns: np.ndarray,
146
+ cov_matrix: np.ndarray,
147
+ risk_free_rate: float,
148
+ ) -> Tuple[np.ndarray, List[np.ndarray]]:
149
+ """
150
+ Generate random portfolios and calculate their standard deviation, returns and Sharpe ratio.
151
+
152
+ Args:
153
+ num_portfolios (int): The number of random portfolios to generate.
154
+
155
+ mean_returns (np.ndarray): The mean (expected) returns of the assets.
156
+
157
+ cov_matrix (np.ndarray): The covariance matrix of the asset returns. Each entry at the
158
+ intersection of a row and a column represents the covariance
159
+ between the returns of the asset corresponding to that row
160
+ and the asset corresponding to that column.
161
+
162
+ risk_free_rate (float): The risk-free rate of return.
163
+
164
+ Returns:
165
+ Tuple of results and weights_record.
166
+
167
+ results (np.ndarray): A 3D array with standard deviation, returns and Sharpe ratio of the portfolios.
168
+
169
+ weights_record (List[np.ndarray]): A list with the weights of the assets in each portfolio.
170
+ """
171
+ # Initialize results array with zeros
172
+ results = np.zeros((3, num_portfolios))
173
+
174
+ # Initialize weights record list
175
+ weights_record = []
176
+
177
+ # Loop over the range of num_portfolios
178
+ for i in np.arange(num_portfolios):
179
+ # Generate random weights
180
+ weights = np.random.random(num_weights)
181
+
182
+ # Normalize weights
183
+ weights /= np.sum(weights)
184
+
185
+ # Record weights
186
+ weights_record.append(weights)
187
+
188
+ # Calculate portfolio standard deviation and returns
189
+ portfolio_std_dev, portfolio_return = portfolio_annualised_performance(
190
+ weights, mean_returns, cov_matrix
191
+ )
192
+
193
+ # Store standard deviation, returns and Sharpe ratio in results
194
+ results[0, i] = portfolio_std_dev
195
+ results[1, i] = portfolio_return
196
+ results[2, i] = (portfolio_return - risk_free_rate) / portfolio_std_dev
197
+
198
+ return results, weights_record
199
+
200
+
201
+ def display_simulated_ef_with_random(
202
+ table: pd.DataFrame,
203
+ mean_returns: List[float],
204
+ cov_matrix: np.ndarray,
205
+ num_portfolios: int,
206
+ risk_free_rate: float,
207
+ ) -> plt.Figure:
208
+ """
209
+ This function displays a simulated efficient frontier plot based on randomly generated portfolios with the specified parameters.
210
+
211
+ Args:
212
+ - mean_returns (List): A list of mean returns for each security or asset in the portfolio.
213
+ - cov_matrix (ndarray): A covariance matrix for the securities or assets in the portfolio.
214
+ - num_portfolios (int): The number of random portfolios to generate.
215
+ - risk_free_rate (float): The risk-free rate of return.
216
+
217
+ Returns:
218
+ - fig (plt.Figure): A pyplot figure object
219
+ """
220
+
221
+ # Generate random portfolios using the specified parameters
222
+ results, weights = random_portfolios(
223
+ num_portfolios, len(mean_returns), mean_returns, cov_matrix, risk_free_rate
224
+ )
225
+
226
+ # Find the maximum Sharpe ratio portfolio and the portfolio with minimum volatility
227
+ max_sharpe_idx = np.argmax(results[2])
228
+ sdp, rp = results[0, max_sharpe_idx], results[1, max_sharpe_idx]
229
+
230
+ # Create a DataFrame of the maximum Sharpe ratio allocation
231
+ max_sharpe_allocation = pd.DataFrame(
232
+ weights[max_sharpe_idx], index=table.columns, columns=["allocation"]
233
+ )
234
+ max_sharpe_allocation.allocation = [
235
+ round(i * 100, 2) for i in max_sharpe_allocation.allocation
236
+ ]
237
+ max_sharpe_allocation = max_sharpe_allocation.T
238
+
239
+ # Find index of the portfolio with minimum volatility
240
+ min_vol_idx = np.argmin(results[0])
241
+ sdp_min, rp_min = results[0, min_vol_idx], results[1, min_vol_idx]
242
+
243
+ # Create a DataFrame of the minimum volatility allocation
244
+ min_vol_allocation = pd.DataFrame(
245
+ weights[min_vol_idx], index=table.columns, columns=["allocation"]
246
+ )
247
+ min_vol_allocation.allocation = [
248
+ round(i * 100, 2) for i in min_vol_allocation.allocation
249
+ ]
250
+ min_vol_allocation = min_vol_allocation.T
251
+
252
+ # Generate and plot the efficient frontier
253
+ fig, ax = plt.subplots(figsize=(10, 7))
254
+ ax.scatter(
255
+ results[0, :],
256
+ results[1, :],
257
+ c=results[2, :],
258
+ cmap="YlGnBu",
259
+ marker="o",
260
+ s=10,
261
+ alpha=0.3,
262
+ )
263
+ ax.scatter(sdp, rp, marker="*", color="r", s=500, label="Maximum Sharpe ratio")
264
+ ax.scatter(
265
+ sdp_min, rp_min, marker="*", color="g", s=500, label="Minimum volatility"
266
+ )
267
+ ax.set_title("Simulated Portfolio Optimization based on Efficient Frontier")
268
+ ax.set_xlabel("Annual volatility")
269
+ ax.set_ylabel("Annual returns")
270
+ ax.legend(labelspacing=0.8)
271
+
272
+ return fig, {
273
+ "Annualised Return": round(rp, 2),
274
+ "Annualised Volatility": round(sdp, 2),
275
+ "Max Sharpe Allocation": max_sharpe_allocation,
276
+ "Max Sharpe Allocation in Percentile": max_sharpe_allocation.div(
277
+ max_sharpe_allocation.sum(axis=1), axis=0
278
+ ),
279
+ "Annualised Return": round(rp_min, 2),
280
+ "Annualised Volatility": round(sdp_min, 2),
281
+ "Min Volatility Allocation": min_vol_allocation,
282
+ "Min Volatility Allocation in Percentile": min_vol_allocation.div(
283
+ min_vol_allocation.sum(axis=1), axis=0
284
+ ),
285
+ }
utils/ui_helper.py CHANGED
@@ -77,6 +77,7 @@ def main_algo_trader():
77
  return df.to_csv().encode("utf-8")
78
 
79
  csv = convert_df(df)
 
80
 
81
  # Create plot
82
  fig = go.Figure()
@@ -171,6 +172,14 @@ def main_algo_trader():
171
  mime="text/csv",
172
  )
173
 
 
 
 
 
 
 
 
 
174
 
175
  # chinese
176
  def main_algo_trader_chinese():
 
77
  return df.to_csv().encode("utf-8")
78
 
79
  csv = convert_df(df)
80
+ recent_selected_stocks = df['portfolio_history'][-1]
81
 
82
  # Create plot
83
  fig = go.Figure()
 
172
  mime="text/csv",
173
  )
174
 
175
+ # Checkpoint: ask user whether they want portfolio weights
176
+ if csv:
177
+ agree = st.sidebar.checkbox('Check the box if you want the weights.')
178
+
179
+ if agree:
180
+ #TODO
181
+ st.markdown(recent_selected_stocks)
182
+
183
 
184
  # chinese
185
  def main_algo_trader_chinese():