Update app.py
Browse files
app.py
CHANGED
|
@@ -449,7 +449,6 @@ def compute(
|
|
| 449 |
pick_band_to_show: str, # "Low" | "Medium" | "High"
|
| 450 |
progress=gr.Progress(track_tqdm=True),
|
| 451 |
):
|
| 452 |
-
# progress UI
|
| 453 |
progress(0.03, desc="Validating tickers...")
|
| 454 |
|
| 455 |
# sanitize table
|
|
@@ -490,7 +489,6 @@ def compute(
|
|
| 490 |
moms = estimate_all_moments_aligned(symbols, years_lookback, rf_ann)
|
| 491 |
betas, covA, erp_ann, sigma_mkt = moms["betas"], moms["cov_ann"], moms["erp_ann"], moms["sigma_m_ann"]
|
| 492 |
|
| 493 |
-
# Weights
|
| 494 |
gross = sum(abs(v) for v in amounts.values())
|
| 495 |
if gross <= 1e-12:
|
| 496 |
return (
|
|
@@ -588,17 +586,12 @@ def compute(
|
|
| 588 |
# -------------- UI --------------
|
| 589 |
with gr.Blocks(
|
| 590 |
title="Efficient Portfolio Advisor",
|
| 591 |
-
css="""
|
| 592 |
-
/* make the left column occupy full width until results are visible */
|
| 593 |
-
"""
|
| 594 |
) as demo:
|
| 595 |
gr.Markdown(
|
| 596 |
"## Efficient Portfolio Advisor\n"
|
| 597 |
"Search symbols, enter **dollar amounts**, set horizon. Returns use Yahoo Finance monthly data; risk-free from FRED."
|
| 598 |
)
|
| 599 |
|
| 600 |
-
# Two-column layout, but right column hidden initially.
|
| 601 |
-
# When right column is hidden, the left column visually fills the page.
|
| 602 |
with gr.Row():
|
| 603 |
with gr.Column(scale=5) as left_col:
|
| 604 |
# --- Vertical flow: Search -> Button -> Matches -> Add ---
|
|
@@ -607,7 +600,6 @@ with gr.Blocks(
|
|
| 607 |
search_note = gr.Markdown()
|
| 608 |
matches = gr.Dropdown(choices=[], label="Matches")
|
| 609 |
add_btn = gr.Button("Add selected to portfolio")
|
| 610 |
-
# ----------------------------------------------------------
|
| 611 |
|
| 612 |
gr.Markdown("### Portfolio positions")
|
| 613 |
table = gr.Dataframe(
|
|
@@ -620,6 +612,9 @@ with gr.Blocks(
|
|
| 620 |
horizon = gr.Number(label="Horizon in years (1–100)", value=HORIZON_YEARS, precision=0)
|
| 621 |
lookback = gr.Slider(1, 15, value=DEFAULT_LOOKBACK_YEARS, step=1, label="Lookback years for betas & covariances")
|
| 622 |
|
|
|
|
|
|
|
|
|
|
| 623 |
# Compute button directly under lookback slider
|
| 624 |
run_btn = gr.Button("Compute (build dataset & suggest)")
|
| 625 |
|
|
@@ -663,8 +658,19 @@ with gr.Blocks(
|
|
| 663 |
table.change(fn=lock_ticker_column, inputs=table, outputs=table)
|
| 664 |
horizon.change(fn=set_horizon, inputs=horizon, outputs=[])
|
| 665 |
|
| 666 |
-
#
|
| 667 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 668 |
fn=compute,
|
| 669 |
inputs=[lookback, table, gr.State("Medium")],
|
| 670 |
outputs=[
|
|
@@ -672,25 +678,27 @@ with gr.Blocks(
|
|
| 672 |
low_txt, med_txt, high_txt,
|
| 673 |
gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
|
| 674 |
gr.State(), gr.State()
|
| 675 |
-
]
|
|
|
|
| 676 |
)
|
| 677 |
|
| 678 |
-
# After compute finishes, reveal all results + Suggestions UI + right column
|
| 679 |
def _reveal():
|
| 680 |
up = gr.update(visible=True)
|
| 681 |
return (up, up, up, up, up, up, up, up, up, up, up, up, up)
|
| 682 |
|
| 683 |
-
compute_evt.then(
|
| 684 |
_reveal,
|
| 685 |
inputs=[],
|
| 686 |
outputs=[
|
| 687 |
-
right_col,
|
| 688 |
plot, summary, positions, sugg_table, dl,
|
| 689 |
sugg_hdr, btn_low, btn_med, btn_high, low_txt, med_txt, high_txt
|
| 690 |
]
|
| 691 |
)
|
| 692 |
|
| 693 |
-
|
|
|
|
|
|
|
| 694 |
btn_low.click(
|
| 695 |
fn=compute,
|
| 696 |
inputs=[lookback, table, gr.State("Low")],
|
|
@@ -699,7 +707,8 @@ with gr.Blocks(
|
|
| 699 |
low_txt, med_txt, high_txt,
|
| 700 |
gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
|
| 701 |
gr.State(), gr.State()
|
| 702 |
-
]
|
|
|
|
| 703 |
)
|
| 704 |
btn_med.click(
|
| 705 |
fn=compute,
|
|
@@ -709,7 +718,8 @@ with gr.Blocks(
|
|
| 709 |
low_txt, med_txt, high_txt,
|
| 710 |
gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
|
| 711 |
gr.State(), gr.State()
|
| 712 |
-
]
|
|
|
|
| 713 |
)
|
| 714 |
btn_high.click(
|
| 715 |
fn=compute,
|
|
@@ -719,7 +729,8 @@ with gr.Blocks(
|
|
| 719 |
low_txt, med_txt, high_txt,
|
| 720 |
gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
|
| 721 |
gr.State(), gr.State()
|
| 722 |
-
]
|
|
|
|
| 723 |
)
|
| 724 |
|
| 725 |
# initialize risk-free at launch
|
|
@@ -727,4 +738,6 @@ RF_CODE = fred_series_for_horizon(HORIZON_YEARS)
|
|
| 727 |
RF_ANN = fetch_fred_yield_annual(RF_CODE)
|
| 728 |
|
| 729 |
if __name__ == "__main__":
|
|
|
|
|
|
|
| 730 |
demo.launch(server_name="0.0.0.0", server_port=7860, show_api=False)
|
|
|
|
| 449 |
pick_band_to_show: str, # "Low" | "Medium" | "High"
|
| 450 |
progress=gr.Progress(track_tqdm=True),
|
| 451 |
):
|
|
|
|
| 452 |
progress(0.03, desc="Validating tickers...")
|
| 453 |
|
| 454 |
# sanitize table
|
|
|
|
| 489 |
moms = estimate_all_moments_aligned(symbols, years_lookback, rf_ann)
|
| 490 |
betas, covA, erp_ann, sigma_mkt = moms["betas"], moms["cov_ann"], moms["erp_ann"], moms["sigma_m_ann"]
|
| 491 |
|
|
|
|
| 492 |
gross = sum(abs(v) for v in amounts.values())
|
| 493 |
if gross <= 1e-12:
|
| 494 |
return (
|
|
|
|
| 586 |
# -------------- UI --------------
|
| 587 |
with gr.Blocks(
|
| 588 |
title="Efficient Portfolio Advisor",
|
|
|
|
|
|
|
|
|
|
| 589 |
) as demo:
|
| 590 |
gr.Markdown(
|
| 591 |
"## Efficient Portfolio Advisor\n"
|
| 592 |
"Search symbols, enter **dollar amounts**, set horizon. Returns use Yahoo Finance monthly data; risk-free from FRED."
|
| 593 |
)
|
| 594 |
|
|
|
|
|
|
|
| 595 |
with gr.Row():
|
| 596 |
with gr.Column(scale=5) as left_col:
|
| 597 |
# --- Vertical flow: Search -> Button -> Matches -> Add ---
|
|
|
|
| 600 |
search_note = gr.Markdown()
|
| 601 |
matches = gr.Dropdown(choices=[], label="Matches")
|
| 602 |
add_btn = gr.Button("Add selected to portfolio")
|
|
|
|
| 603 |
|
| 604 |
gr.Markdown("### Portfolio positions")
|
| 605 |
table = gr.Dataframe(
|
|
|
|
| 612 |
horizon = gr.Number(label="Horizon in years (1–100)", value=HORIZON_YEARS, precision=0)
|
| 613 |
lookback = gr.Slider(1, 15, value=DEFAULT_LOOKBACK_YEARS, step=1, label="Lookback years for betas & covariances")
|
| 614 |
|
| 615 |
+
# Loading banner (appears instantly on click, hidden after results show)
|
| 616 |
+
loading_md = gr.Markdown("⏳ Working... fetching data and computing suggestions...", visible=False)
|
| 617 |
+
|
| 618 |
# Compute button directly under lookback slider
|
| 619 |
run_btn = gr.Button("Compute (build dataset & suggest)")
|
| 620 |
|
|
|
|
| 658 |
table.change(fn=lock_ticker_column, inputs=table, outputs=table)
|
| 659 |
horizon.change(fn=set_horizon, inputs=horizon, outputs=[])
|
| 660 |
|
| 661 |
+
# Small helpers to toggle the loading banner
|
| 662 |
+
def _show_loading():
|
| 663 |
+
return gr.update(visible=True)
|
| 664 |
+
def _hide_loading():
|
| 665 |
+
return gr.update(visible=False)
|
| 666 |
+
|
| 667 |
+
# 1) Show loading immediately
|
| 668 |
+
# 2) Run compute (with full-page progress bar)
|
| 669 |
+
# 3) Reveal results and suggestion controls
|
| 670 |
+
# 4) Hide loading banner
|
| 671 |
+
show_evt = run_btn.click(_show_loading, inputs=[], outputs=[loading_md])
|
| 672 |
+
|
| 673 |
+
compute_evt = show_evt.then(
|
| 674 |
fn=compute,
|
| 675 |
inputs=[lookback, table, gr.State("Medium")],
|
| 676 |
outputs=[
|
|
|
|
| 678 |
low_txt, med_txt, high_txt,
|
| 679 |
gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
|
| 680 |
gr.State(), gr.State()
|
| 681 |
+
],
|
| 682 |
+
show_progress="full", # <-- full-page progress overlay
|
| 683 |
)
|
| 684 |
|
|
|
|
| 685 |
def _reveal():
|
| 686 |
up = gr.update(visible=True)
|
| 687 |
return (up, up, up, up, up, up, up, up, up, up, up, up, up)
|
| 688 |
|
| 689 |
+
after_compute = compute_evt.then(
|
| 690 |
_reveal,
|
| 691 |
inputs=[],
|
| 692 |
outputs=[
|
| 693 |
+
right_col,
|
| 694 |
plot, summary, positions, sugg_table, dl,
|
| 695 |
sugg_hdr, btn_low, btn_med, btn_high, low_txt, med_txt, high_txt
|
| 696 |
]
|
| 697 |
)
|
| 698 |
|
| 699 |
+
after_compute.then(_hide_loading, inputs=[], outputs=[loading_md])
|
| 700 |
+
|
| 701 |
+
# Band buttons (after reveal). Also request full progress overlay.
|
| 702 |
btn_low.click(
|
| 703 |
fn=compute,
|
| 704 |
inputs=[lookback, table, gr.State("Low")],
|
|
|
|
| 707 |
low_txt, med_txt, high_txt,
|
| 708 |
gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
|
| 709 |
gr.State(), gr.State()
|
| 710 |
+
],
|
| 711 |
+
show_progress="full",
|
| 712 |
)
|
| 713 |
btn_med.click(
|
| 714 |
fn=compute,
|
|
|
|
| 718 |
low_txt, med_txt, high_txt,
|
| 719 |
gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
|
| 720 |
gr.State(), gr.State()
|
| 721 |
+
],
|
| 722 |
+
show_progress="full",
|
| 723 |
)
|
| 724 |
btn_high.click(
|
| 725 |
fn=compute,
|
|
|
|
| 729 |
low_txt, med_txt, high_txt,
|
| 730 |
gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(), gr.State(),
|
| 731 |
gr.State(), gr.State()
|
| 732 |
+
],
|
| 733 |
+
show_progress="full",
|
| 734 |
)
|
| 735 |
|
| 736 |
# initialize risk-free at launch
|
|
|
|
| 738 |
RF_ANN = fetch_fred_yield_annual(RF_CODE)
|
| 739 |
|
| 740 |
if __name__ == "__main__":
|
| 741 |
+
# enable event queue so progress bar + async UI updates work properly
|
| 742 |
+
demo.queue()
|
| 743 |
demo.launch(server_name="0.0.0.0", server_port=7860, show_api=False)
|