abnsol commited on
Commit
2e24ccc
·
verified ·
1 Parent(s): f276afb

Update src/optimization.py

Browse files
Files changed (1) hide show
  1. src/optimization.py +31 -37
src/optimization.py CHANGED
@@ -1,21 +1,25 @@
1
- # src/optimization.py
2
  import os
3
  import pandas as pd
4
  import numpy as np
 
5
  from pypfopt.efficient_frontier import EfficientFrontier
6
  from pypfopt import risk_models, expected_returns, plotting
7
  import matplotlib.pyplot as plt
8
 
9
  TRADING_DAYS = 252
10
 
11
- def load_processed_prices(proc_dir: str, tickers: list[str]) -> dict[str, pd.DataFrame]:
12
- """Load processed CSVs for each ticker."""
 
 
13
  data = {}
14
- for t in tickers:
15
- fn = os.path.join(proc_dir, f"{t}_processed.csv")
16
- df = pd.read_csv(fn, parse_dates=["Date"], index_col="Date")
17
  df = df.sort_index()
18
- data[t] = df
 
 
 
19
  return data
20
 
21
 
@@ -104,30 +108,22 @@ def optimize_portfolio(mu: pd.Series,
104
 
105
  def optimize_for_target(mu: pd.Series,
106
  cov: pd.DataFrame,
107
- target_type: str = "return",
108
- target_value: float = 0.20) -> dict:
 
109
  """
110
- Optimize portfolio for a specific target return or volatility.
111
-
112
- Args:
113
- mu (pd.Series): Expected annual returns.
114
- cov (pd.DataFrame): Annualized covariance matrix.
115
- target_type (str): 'return' or 'volatility'.
116
- target_value (float): The desired target value (e.g., 0.20 for 20%).
117
-
118
- Returns:
119
- dict with portfolio weights and performance.
120
  """
121
- ef = EfficientFrontier(mu, cov)
122
 
123
  try:
124
- if target_type == "return":
125
  weights = ef.efficient_return(target_value)
126
- elif target_type == "volatility":
127
  weights = ef.efficient_risk(target_value)
128
  else:
129
- raise ValueError("target_type must be 'return' or 'volatility'")
130
-
131
  cleaned_weights = ef.clean_weights()
132
  perf = ef.portfolio_performance(verbose=False)
133
 
@@ -139,19 +135,13 @@ def optimize_for_target(mu: pd.Series,
139
  "sharpe": perf[2],
140
  }
141
  }
142
- except ValueError as e:
143
- # Handle cases where the target is not achievable
144
  return {"weights": {}, "performance": {"error": str(e)}}
145
 
146
 
147
  def plot_efficient_frontier(mu, cov, results: dict):
148
  """
149
- Plot the efficient frontier and mark key portfolios.
150
-
151
- Args:
152
- mu (pd.Series): Expected annual returns.
153
- cov (pd.DataFrame): Annualized covariance matrix.
154
- results (dict): Dictionary containing the results from optimize_portfolio.
155
  """
156
  ef = EfficientFrontier(mu, cov)
157
  fig, ax = plt.subplots(figsize=(8, 6))
@@ -159,13 +149,17 @@ def plot_efficient_frontier(mu, cov, results: dict):
159
  # Plot the frontier
160
  plotting.plot_efficient_frontier(ef, ax=ax, show_assets=False)
161
 
162
- # Plot Max Sharpe portfolio
163
- sharpe_perf = results["perf_sharpe"]
164
- ax.scatter(sharpe_perf["volatility"], sharpe_perf["return"], marker="*", s=150, c="r", label="Max Sharpe")
165
 
166
- # Plot Min Volatility portfolio
 
167
  minvol_perf = results["perf_minvol"]
168
- ax.scatter(minvol_perf["volatility"], minvol_perf["return"], marker="P", s=150, c="g", label="Min Volatility")
 
 
 
169
 
170
  ax.set_title("Efficient Frontier")
171
  ax.legend()
 
 
1
  import os
2
  import pandas as pd
3
  import numpy as np
4
+ import yfinance as yf
5
  from pypfopt.efficient_frontier import EfficientFrontier
6
  from pypfopt import risk_models, expected_returns, plotting
7
  import matplotlib.pyplot as plt
8
 
9
  TRADING_DAYS = 252
10
 
11
+ def fetch_live_prices(tickers: list[str], start_date: str, end_date: str) -> dict[str, pd.DataFrame]:
12
+ """
13
+ Fetch live stock prices from yfinance for a list of tickers.
14
+ """
15
  data = {}
16
+ for ticker in tickers:
17
+ df = yf.download(ticker, start=start_date, end=end_date, progress=False)
 
18
  df = df.sort_index()
19
+ if df.empty:
20
+ st.error(f"Could not fetch data for {ticker}. Please check the ticker symbol.")
21
+ st.stop()
22
+ data[ticker] = df
23
  return data
24
 
25
 
 
108
 
109
  def optimize_for_target(mu: pd.Series,
110
  cov: pd.DataFrame,
111
+ target_type: str,
112
+ target_value: float,
113
+ bounds: tuple = (0, 1)) -> dict:
114
  """
115
+ Optimize for a specific target return or volatility.
 
 
 
 
 
 
 
 
 
116
  """
117
+ ef = EfficientFrontier(mu, cov, weight_bounds=bounds)
118
 
119
  try:
120
+ if target_type == 'return':
121
  weights = ef.efficient_return(target_value)
122
+ elif target_type == 'volatility':
123
  weights = ef.efficient_risk(target_value)
124
  else:
125
+ return {"error": "Invalid target_type. Use 'return' or 'volatility'."}
126
+
127
  cleaned_weights = ef.clean_weights()
128
  perf = ef.portfolio_performance(verbose=False)
129
 
 
135
  "sharpe": perf[2],
136
  }
137
  }
138
+ except Exception as e:
 
139
  return {"weights": {}, "performance": {"error": str(e)}}
140
 
141
 
142
  def plot_efficient_frontier(mu, cov, results: dict):
143
  """
144
+ Plot the efficient frontier with key portfolios marked.
 
 
 
 
 
145
  """
146
  ef = EfficientFrontier(mu, cov)
147
  fig, ax = plt.subplots(figsize=(8, 6))
 
149
  # Plot the frontier
150
  plotting.plot_efficient_frontier(ef, ax=ax, show_assets=False)
151
 
152
+ # Get weights for plotting
153
+ sharpe_weights = np.array(list(results["weights_sharpe"].values()))
154
+ minvol_weights = np.array(list(results["weights_minvol"].values()))
155
 
156
+ # Find portfolio returns/volatility for plotting
157
+ sharpe_perf = results["perf_sharpe"]
158
  minvol_perf = results["perf_minvol"]
159
+
160
+ # Plot markers
161
+ ax.scatter(sharpe_perf["volatility"], sharpe_perf["return"], marker="*", color="r", s=250, label="Max Sharpe")
162
+ ax.scatter(minvol_perf["volatility"], minvol_perf["return"], marker="X", color="g", s=200, label="Min Volatility")
163
 
164
  ax.set_title("Efficient Frontier")
165
  ax.legend()