Update app.py
Browse files
app.py
CHANGED
|
@@ -370,7 +370,7 @@ def suggest_one_per_band(synth: pd.DataFrame, sigma_mkt: float, universe_user: L
|
|
| 370 |
out[band.lower()] = chosen
|
| 371 |
return out
|
| 372 |
|
| 373 |
-
# -------------- UI helpers --------------
|
| 374 |
def empty_positions_df():
|
| 375 |
return pd.DataFrame(columns=["ticker", "amount_usd", "weight_exposure", "beta"])
|
| 376 |
|
|
@@ -386,6 +386,47 @@ def set_horizon(years: float):
|
|
| 386 |
RF_CODE = code
|
| 387 |
RF_ANN = rf
|
| 388 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 389 |
# -------------- main compute --------------
|
| 390 |
UNIVERSE: List[str] = [MARKET_TICKER, "QQQ", "VTI", "SOXX", "IBIT"]
|
| 391 |
|
|
@@ -417,13 +458,10 @@ def compute(
|
|
| 417 |
|
| 418 |
symbols = [t for t in df["ticker"].tolist() if t]
|
| 419 |
if len(symbols) == 0:
|
| 420 |
-
# hide suggestions
|
| 421 |
hide = gr.update(visible=False)
|
| 422 |
return (
|
| 423 |
None, "Add at least one ticker.", empty_positions_df(), empty_suggestion_df(), None,
|
| 424 |
-
|
| 425 |
-
gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), # low/med/high text
|
| 426 |
-
gr.update(visible=False), gr.update(visible=False), gr.update(visible=False) # buttons
|
| 427 |
)
|
| 428 |
|
| 429 |
symbols = validate_tickers(symbols, years_lookback)
|
|
@@ -462,7 +500,7 @@ def compute(
|
|
| 462 |
a_sigma, b_sigma, mu_eff_same_sigma = efficient_same_sigma(sigma_hist, rf_ann, erp_ann, sigma_mkt)
|
| 463 |
a_mu, b_mu, sigma_eff_same_mu = efficient_same_return(mu_capm, rf_ann, erp_ann, sigma_mkt)
|
| 464 |
|
| 465 |
-
# Synthetic dataset & suggestions
|
| 466 |
user_universe = list(symbols)
|
| 467 |
synth = build_synthetic_dataset(user_universe, covA, betas, rf_ann, erp_ann, sigma_mkt, n_rows=SYNTH_ROWS)
|
| 468 |
csv_path = os.path.join(DATA_DIR, f"investor_profiles_{int(time.time())}.csv")
|
|
@@ -510,7 +548,6 @@ def compute(
|
|
| 510 |
sugg_sigma_hist=chosen_sigma, sugg_mu_capm=chosen_mu
|
| 511 |
)
|
| 512 |
|
| 513 |
-
# summary text
|
| 514 |
info = "\n".join([
|
| 515 |
"### Inputs",
|
| 516 |
f"- Lookback years {years_lookback}",
|
|
@@ -531,7 +568,6 @@ def compute(
|
|
| 531 |
"Weights may exceed 1 (leverage) or be negative (borrowing). If leverage isn’t allowed, scale toward 1.0."
|
| 532 |
])
|
| 533 |
|
| 534 |
-
# show Suggestions section now
|
| 535 |
show = gr.update(visible=True)
|
| 536 |
return (
|
| 537 |
img, info, pos_table, sugg_table, csv_path,
|
|
|
|
| 370 |
out[band.lower()] = chosen
|
| 371 |
return out
|
| 372 |
|
| 373 |
+
# -------------- UI helpers (restored) --------------
|
| 374 |
def empty_positions_df():
|
| 375 |
return pd.DataFrame(columns=["ticker", "amount_usd", "weight_exposure", "beta"])
|
| 376 |
|
|
|
|
| 386 |
RF_CODE = code
|
| 387 |
RF_ANN = rf
|
| 388 |
|
| 389 |
+
def search_tickers_cb(q: str):
|
| 390 |
+
opts = yahoo_search(q)
|
| 391 |
+
note = "Select a symbol and click 'Add selected to portfolio'." if opts else "No matches."
|
| 392 |
+
return note, gr.update(choices=opts, value=None)
|
| 393 |
+
|
| 394 |
+
def add_symbol(selection: str, table: Optional[pd.DataFrame]):
|
| 395 |
+
if not selection:
|
| 396 |
+
return table if isinstance(table, pd.DataFrame) else pd.DataFrame(columns=["ticker","amount_usd"]), "Pick a row in Matches first."
|
| 397 |
+
symbol = selection.split("|")[0].strip().upper()
|
| 398 |
+
|
| 399 |
+
current = []
|
| 400 |
+
if isinstance(table, pd.DataFrame) and not table.empty:
|
| 401 |
+
current = [str(x).upper() for x in table["ticker"].tolist() if str(x) != "nan"]
|
| 402 |
+
tickers = current if symbol in current else current + [symbol]
|
| 403 |
+
|
| 404 |
+
val = validate_tickers(tickers, years=DEFAULT_LOOKBACK_YEARS)
|
| 405 |
+
tickers = [t for t in tickers if t in val]
|
| 406 |
+
|
| 407 |
+
amt_map = {}
|
| 408 |
+
if isinstance(table, pd.DataFrame) and not table.empty:
|
| 409 |
+
for _, r in table.iterrows():
|
| 410 |
+
t = str(r.get("ticker", "")).upper()
|
| 411 |
+
if t in tickers:
|
| 412 |
+
amt_map[t] = float(pd.to_numeric(r.get("amount_usd", 0.0), errors="coerce") or 0.0)
|
| 413 |
+
|
| 414 |
+
new_table = pd.DataFrame({"ticker": tickers, "amount_usd": [amt_map.get(t, 0.0) for t in tickers]})
|
| 415 |
+
if len(new_table) > MAX_TICKERS:
|
| 416 |
+
new_table = new_table.iloc[:MAX_TICKERS]
|
| 417 |
+
return new_table, f"Reached max of {MAX_TICKERS}."
|
| 418 |
+
return new_table, f"Added {symbol}."
|
| 419 |
+
|
| 420 |
+
def lock_ticker_column(tb: Optional[pd.DataFrame]):
|
| 421 |
+
if not isinstance(tb, pd.DataFrame) or tb.empty:
|
| 422 |
+
return pd.DataFrame(columns=["ticker", "amount_usd"])
|
| 423 |
+
tickers = [str(x).upper() for x in tb["ticker"].tolist()]
|
| 424 |
+
amounts = pd.to_numeric(tb["amount_usd"], errors="coerce").fillna(0.0).tolist()
|
| 425 |
+
val = validate_tickers(tickers, years=DEFAULT_LOOKBACK_YEARS)
|
| 426 |
+
tickers = [t for t in tickers if t in val]
|
| 427 |
+
amounts = amounts[:len(tickers)] + [0.0] * max(0, len(tickers) - len(amounts))
|
| 428 |
+
return pd.DataFrame({"ticker": tickers, "amount_usd": amounts})
|
| 429 |
+
|
| 430 |
# -------------- main compute --------------
|
| 431 |
UNIVERSE: List[str] = [MARKET_TICKER, "QQQ", "VTI", "SOXX", "IBIT"]
|
| 432 |
|
|
|
|
| 458 |
|
| 459 |
symbols = [t for t in df["ticker"].tolist() if t]
|
| 460 |
if len(symbols) == 0:
|
|
|
|
| 461 |
hide = gr.update(visible=False)
|
| 462 |
return (
|
| 463 |
None, "Add at least one ticker.", empty_positions_df(), empty_suggestion_df(), None,
|
| 464 |
+
hide, hide, hide, hide, hide, hide, hide
|
|
|
|
|
|
|
| 465 |
)
|
| 466 |
|
| 467 |
symbols = validate_tickers(symbols, years_lookback)
|
|
|
|
| 500 |
a_sigma, b_sigma, mu_eff_same_sigma = efficient_same_sigma(sigma_hist, rf_ann, erp_ann, sigma_mkt)
|
| 501 |
a_mu, b_mu, sigma_eff_same_mu = efficient_same_return(mu_capm, rf_ann, erp_ann, sigma_mkt)
|
| 502 |
|
| 503 |
+
# Synthetic dataset & suggestions
|
| 504 |
user_universe = list(symbols)
|
| 505 |
synth = build_synthetic_dataset(user_universe, covA, betas, rf_ann, erp_ann, sigma_mkt, n_rows=SYNTH_ROWS)
|
| 506 |
csv_path = os.path.join(DATA_DIR, f"investor_profiles_{int(time.time())}.csv")
|
|
|
|
| 548 |
sugg_sigma_hist=chosen_sigma, sugg_mu_capm=chosen_mu
|
| 549 |
)
|
| 550 |
|
|
|
|
| 551 |
info = "\n".join([
|
| 552 |
"### Inputs",
|
| 553 |
f"- Lookback years {years_lookback}",
|
|
|
|
| 568 |
"Weights may exceed 1 (leverage) or be negative (borrowing). If leverage isn’t allowed, scale toward 1.0."
|
| 569 |
])
|
| 570 |
|
|
|
|
| 571 |
show = gr.update(visible=True)
|
| 572 |
return (
|
| 573 |
img, info, pos_table, sugg_table, csv_path,
|