Tulitula commited on
Commit
c7ea09b
·
verified ·
1 Parent(s): 54bd91f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +28 -22
app.py CHANGED
@@ -361,12 +361,11 @@ def suggest_one_per_band(synth: pd.DataFrame, sigma_mkt: float, universe_user: L
361
  lo, hi = _band_bounds(sigma_mkt, band)
362
  pool = synth[(synth["sigma_hist"] >= lo) & (synth["sigma_hist"] <= hi)].copy()
363
  if pool.empty:
364
- # choose reasonable substitutes quietly (no extra text)
365
  if band.lower() == "low":
366
  pool = synth.nsmallest(50, "sigma_hist").copy()
367
  elif band.lower() == "high":
368
  pool = synth.nlargest(50, "sigma_hist").copy()
369
- else: # medium
370
  tmp = synth.copy()
371
  tmp["dist_med"] = (tmp["sigma_hist"] - sigma_mkt).abs()
372
  pool = tmp.nsmallest(100, "dist_med").drop(columns=["dist_med"])
@@ -389,7 +388,7 @@ def set_horizon(years: float):
389
  HORIZON_YEARS = y
390
  RF_CODE = code
391
  RF_ANN = rf
392
- return f"Risk-free series {code}. Latest annual rate {rf:.2%}."
393
 
394
  def search_tickers_cb(q: str):
395
  opts = yahoo_search(q)
@@ -463,13 +462,19 @@ def compute(
463
 
464
  symbols = [t for t in df["ticker"].tolist() if t]
465
  if len(symbols) == 0:
466
- return None, "Add at least one ticker.", "Universe empty.", empty_positions_df(), empty_suggestion_df(), None, \
467
- "", "", "", None, None, None, None, None, None, None, None, None
 
 
 
468
 
469
  symbols = validate_tickers(symbols, years_lookback)
470
  if len(symbols) == 0:
471
- return None, "Could not validate any tickers.", "Universe invalid.", empty_positions_df(), empty_suggestion_df(), None, \
472
- "", "", "", None, None, None, None, None, None, None, None, None
 
 
 
473
 
474
  global UNIVERSE
475
  UNIVERSE = list(sorted(set(symbols)))[:MAX_TICKERS]
@@ -485,8 +490,11 @@ def compute(
485
  # Weights
486
  gross = sum(abs(v) for v in amounts.values())
487
  if gross <= 1e-12:
488
- return None, "All amounts are zero.", "Universe ok.", empty_positions_df(), empty_suggestion_df(), None, \
489
- "", "", "", None, None, None, None, None, None, None, None, None
 
 
 
490
  weights = {k: v / gross for k, v in amounts.items()}
491
 
492
  # Portfolio CAPM stats
@@ -545,7 +553,7 @@ def compute(
545
  sugg_sigma_hist=chosen_sigma, sugg_mu_capm=chosen_mu
546
  )
547
 
548
- # ---------- summary text (cosmetic-only changes made) ----------
549
  info = "\n".join([
550
  "### Inputs",
551
  f"- Lookback years {years_lookback}",
@@ -565,14 +573,11 @@ def compute(
565
  "_How to replicate:_ use a broad market ETF (e.g., VOO) for the **Market** leg and a T-bill/money-market fund for **Bills**. ",
566
  "Weights can be >1 or negative (e.g., Market > 1 and Bills < 0 implies leverage/borrowing). ",
567
  "If leverage isn’t allowed, scale both weights proportionally toward 1.0 to fit your constraints.",
568
- "",
569
- "_All points are on/under the CML for display (y clamped to CML at given σ)._"
570
  ])
571
- # -------------------------------------------------------------------
572
 
573
- uni_msg = f"Universe set to: {', '.join(UNIVERSE)}"
574
  return (
575
- img, info, uni_msg, pos_table, sugg_table, csv_path,
576
  txt_low, txt_med, txt_high,
577
  rf_ann, erp_ann, sigma_mkt, sigma_hist, mu_capm, mu_eff_same_sigma, sigma_eff_same_mu,
578
  chosen_sigma, chosen_mu
@@ -619,7 +624,6 @@ with gr.Blocks(title="Efficient Portfolio Advisor") as demo:
619
  with gr.Column(scale=1):
620
  plot = gr.Image(label="Capital Market Line (CAPM)", type="pil")
621
  summary = gr.Markdown(label="Inputs & Results")
622
- universe_msg = gr.Textbox(label="Universe status", interactive=False)
623
  positions = gr.Dataframe(
624
  label="Computed positions",
625
  headers=["ticker", "amount_usd", "weight_exposure", "beta"],
@@ -638,18 +642,20 @@ with gr.Blocks(title="Efficient Portfolio Advisor") as demo:
638
  )
639
  dl = gr.File(label="Generated dataset CSV", value=None, visible=True)
640
 
641
- # wire search / add / locking / horizon
642
  search_btn.click(fn=search_tickers_cb, inputs=q, outputs=[search_note, matches])
643
  add_btn.click(fn=add_symbol, inputs=[matches, table], outputs=[table, search_note])
644
  table.change(fn=lock_ticker_column, inputs=table, outputs=table)
645
- horizon.change(fn=set_horizon, inputs=horizon, outputs=universe_msg)
 
 
646
 
647
  # compute + render (default to Medium band)
648
  run_btn.click(
649
  fn=compute,
650
  inputs=[lookback, table, gr.State("Medium")],
651
  outputs=[
652
- plot, summary, universe_msg, positions, sugg_table, dl,
653
  low_txt, med_txt, high_txt,
654
  gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
655
  gr.State(), gr.State()
@@ -661,7 +667,7 @@ with gr.Blocks(title="Efficient Portfolio Advisor") as demo:
661
  fn=compute,
662
  inputs=[lookback, table, gr.State("Low")],
663
  outputs=[
664
- plot, summary, universe_msg, positions, sugg_table, dl,
665
  low_txt, med_txt, high_txt,
666
  gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
667
  gr.State(), gr.State()
@@ -671,7 +677,7 @@ with gr.Blocks(title="Efficient Portfolio Advisor") as demo:
671
  fn=compute,
672
  inputs=[lookback, table, gr.State("Medium")],
673
  outputs=[
674
- plot, summary, universe_msg, positions, sugg_table, dl,
675
  low_txt, med_txt, high_txt,
676
  gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
677
  gr.State(), gr.State()
@@ -681,7 +687,7 @@ with gr.Blocks(title="Efficient Portfolio Advisor") as demo:
681
  fn=compute,
682
  inputs=[lookback, table, gr.State("High")],
683
  outputs=[
684
- plot, summary, universe_msg, positions, sugg_table, dl,
685
  low_txt, med_txt, high_txt,
686
  gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
687
  gr.State(), gr.State()
 
361
  lo, hi = _band_bounds(sigma_mkt, band)
362
  pool = synth[(synth["sigma_hist"] >= lo) & (synth["sigma_hist"] <= hi)].copy()
363
  if pool.empty:
 
364
  if band.lower() == "low":
365
  pool = synth.nsmallest(50, "sigma_hist").copy()
366
  elif band.lower() == "high":
367
  pool = synth.nlargest(50, "sigma_hist").copy()
368
+ else:
369
  tmp = synth.copy()
370
  tmp["dist_med"] = (tmp["sigma_hist"] - sigma_mkt).abs()
371
  pool = tmp.nsmallest(100, "dist_med").drop(columns=["dist_med"])
 
388
  HORIZON_YEARS = y
389
  RF_CODE = code
390
  RF_ANN = rf
391
+ # no UI text returned (we removed the "Universe status" box)
392
 
393
  def search_tickers_cb(q: str):
394
  opts = yahoo_search(q)
 
462
 
463
  symbols = [t for t in df["ticker"].tolist() if t]
464
  if len(symbols) == 0:
465
+ return (
466
+ None, "Add at least one ticker.", empty_positions_df(), empty_suggestion_df(), None,
467
+ "", "", "",
468
+ None, None, None, None, None, None, None, None, None
469
+ )
470
 
471
  symbols = validate_tickers(symbols, years_lookback)
472
  if len(symbols) == 0:
473
+ return (
474
+ None, "Could not validate any tickers.", empty_positions_df(), empty_suggestion_df(), None,
475
+ "", "", "",
476
+ None, None, None, None, None, None, None, None, None
477
+ )
478
 
479
  global UNIVERSE
480
  UNIVERSE = list(sorted(set(symbols)))[:MAX_TICKERS]
 
490
  # Weights
491
  gross = sum(abs(v) for v in amounts.values())
492
  if gross <= 1e-12:
493
+ return (
494
+ None, "All amounts are zero.", empty_positions_df(), empty_suggestion_df(), None,
495
+ "", "", "",
496
+ rf_ann, erp_ann, sigma_mkt, None, None, None, None, None, None
497
+ )
498
  weights = {k: v / gross for k, v in amounts.items()}
499
 
500
  # Portfolio CAPM stats
 
553
  sugg_sigma_hist=chosen_sigma, sugg_mu_capm=chosen_mu
554
  )
555
 
556
+ # ---------- summary text (cleaned copy only) ----------
557
  info = "\n".join([
558
  "### Inputs",
559
  f"- Lookback years {years_lookback}",
 
573
  "_How to replicate:_ use a broad market ETF (e.g., VOO) for the **Market** leg and a T-bill/money-market fund for **Bills**. ",
574
  "Weights can be >1 or negative (e.g., Market > 1 and Bills < 0 implies leverage/borrowing). ",
575
  "If leverage isn’t allowed, scale both weights proportionally toward 1.0 to fit your constraints.",
 
 
576
  ])
577
+ # ------------------------------------------------------
578
 
 
579
  return (
580
+ img, info, pos_table, sugg_table, csv_path,
581
  txt_low, txt_med, txt_high,
582
  rf_ann, erp_ann, sigma_mkt, sigma_hist, mu_capm, mu_eff_same_sigma, sigma_eff_same_mu,
583
  chosen_sigma, chosen_mu
 
624
  with gr.Column(scale=1):
625
  plot = gr.Image(label="Capital Market Line (CAPM)", type="pil")
626
  summary = gr.Markdown(label="Inputs & Results")
 
627
  positions = gr.Dataframe(
628
  label="Computed positions",
629
  headers=["ticker", "amount_usd", "weight_exposure", "beta"],
 
642
  )
643
  dl = gr.File(label="Generated dataset CSV", value=None, visible=True)
644
 
645
+ # wire search / add / locking
646
  search_btn.click(fn=search_tickers_cb, inputs=q, outputs=[search_note, matches])
647
  add_btn.click(fn=add_symbol, inputs=[matches, table], outputs=[table, search_note])
648
  table.change(fn=lock_ticker_column, inputs=table, outputs=table)
649
+
650
+ # horizon updates globals silently (no UI output)
651
+ horizon.change(fn=set_horizon, inputs=horizon, outputs=[])
652
 
653
  # compute + render (default to Medium band)
654
  run_btn.click(
655
  fn=compute,
656
  inputs=[lookback, table, gr.State("Medium")],
657
  outputs=[
658
+ plot, summary, positions, sugg_table, dl,
659
  low_txt, med_txt, high_txt,
660
  gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
661
  gr.State(), gr.State()
 
667
  fn=compute,
668
  inputs=[lookback, table, gr.State("Low")],
669
  outputs=[
670
+ plot, summary, positions, sugg_table, dl,
671
  low_txt, med_txt, high_txt,
672
  gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
673
  gr.State(), gr.State()
 
677
  fn=compute,
678
  inputs=[lookback, table, gr.State("Medium")],
679
  outputs=[
680
+ plot, summary, positions, sugg_table, dl,
681
  low_txt, med_txt, high_txt,
682
  gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
683
  gr.State(), gr.State()
 
687
  fn=compute,
688
  inputs=[lookback, table, gr.State("High")],
689
  outputs=[
690
+ plot, summary, positions, sugg_table, dl,
691
  low_txt, med_txt, high_txt,
692
  gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
693
  gr.State(), gr.State()