Update app.py
Browse files
app.py
CHANGED
|
@@ -481,36 +481,44 @@ def search_tickers_cb(q: str):
|
|
| 481 |
opts = [f"{h['symbol']} | {h['name']} | {h['exchange']}" for h in hits]
|
| 482 |
return "Select a symbol and click Add", opts
|
| 483 |
|
|
|
|
| 484 |
def add_symbol(selection: str, table: pd.DataFrame):
|
| 485 |
if not selection:
|
| 486 |
return table, "Pick a row from Matches first"
|
| 487 |
symbol = selection.split("|")[0].strip().upper()
|
| 488 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 489 |
tickers = current if symbol in current else current + [symbol]
|
| 490 |
-
|
| 491 |
-
tickers = [t for t in tickers if t in val]
|
| 492 |
amt_map = {}
|
| 493 |
if table is not None and len(table) > 0:
|
| 494 |
for _, r in table.iterrows():
|
| 495 |
t = str(r.get("ticker", "")).upper()
|
| 496 |
if t in tickers:
|
| 497 |
amt_map[t] = float(pd.to_numeric(r.get("amount_usd", 0.0), errors="coerce") or 0.0)
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 502 |
msg = f"Reached max of {MAX_TICKERS}"
|
| 503 |
return new_table, msg
|
| 504 |
|
|
|
|
| 505 |
def lock_ticker_column(tb: pd.DataFrame):
|
| 506 |
if tb is None or len(tb) == 0:
|
| 507 |
return pd.DataFrame(columns=["ticker", "amount_usd"])
|
| 508 |
-
tickers = [str(x).upper() for x in tb["ticker"].tolist()]
|
| 509 |
amounts = pd.to_numeric(tb["amount_usd"], errors="coerce").fillna(0.0).tolist()
|
| 510 |
-
val = validate_tickers(tickers, years=DEFAULT_LOOKBACK_YEARS)
|
| 511 |
-
tickers = [t for t in tickers if t in val]
|
| 512 |
amounts = amounts[:len(tickers)] + [0.0] * max(0, len(tickers) - len(amounts))
|
| 513 |
-
return pd.DataFrame({"ticker": tickers, "amount_usd": amounts})
|
| 514 |
|
| 515 |
def set_horizon(years: float):
|
| 516 |
y = max(1.0, min(100.0, float(years)))
|
|
@@ -533,9 +541,10 @@ def compute(years_lookback: int, table: pd.DataFrame,
|
|
| 533 |
if len(symbols) == 0:
|
| 534 |
return None, "Add at least one ticker", "Universe empty", empty_positions_df(), empty_suggest_df(), None
|
| 535 |
|
|
|
|
| 536 |
symbols = validate_tickers(symbols, years_lookback)
|
| 537 |
if len(symbols) == 0:
|
| 538 |
-
return None, "Could not validate any tickers", "Universe invalid", empty_positions_df(), empty_suggest_df(), None
|
| 539 |
|
| 540 |
global UNIVERSE
|
| 541 |
UNIVERSE = list(sorted(set([s for s in symbols if s != MARKET_TICKER] + [MARKET_TICKER])))[:MAX_TICKERS]
|
|
|
|
| 481 |
opts = [f"{h['symbol']} | {h['name']} | {h['exchange']}" for h in hits]
|
| 482 |
return "Select a symbol and click Add", opts
|
| 483 |
|
| 484 |
+
# PATCH 1: lenient add (no validation/network)
|
| 485 |
def add_symbol(selection: str, table: pd.DataFrame):
|
| 486 |
if not selection:
|
| 487 |
return table, "Pick a row from Matches first"
|
| 488 |
symbol = selection.split("|")[0].strip().upper()
|
| 489 |
+
|
| 490 |
+
current = [] if table is None or len(table) == 0 else [
|
| 491 |
+
str(x).upper() for x in table["ticker"].tolist() if str(x) != "nan"
|
| 492 |
+
]
|
| 493 |
+
# de-dup and append without validation here
|
| 494 |
tickers = current if symbol in current else current + [symbol]
|
| 495 |
+
|
|
|
|
| 496 |
amt_map = {}
|
| 497 |
if table is not None and len(table) > 0:
|
| 498 |
for _, r in table.iterrows():
|
| 499 |
t = str(r.get("ticker", "")).upper()
|
| 500 |
if t in tickers:
|
| 501 |
amt_map[t] = float(pd.to_numeric(r.get("amount_usd", 0.0), errors="coerce") or 0.0)
|
| 502 |
+
|
| 503 |
+
tickers = tickers[:MAX_TICKERS]
|
| 504 |
+
new_table = pd.DataFrame({
|
| 505 |
+
"ticker": tickers,
|
| 506 |
+
"amount_usd": [amt_map.get(t, 0.0) for t in tickers]
|
| 507 |
+
})
|
| 508 |
+
|
| 509 |
+
msg = f"Added {symbol}" if symbol in new_table["ticker"].tolist() else f"{symbol} not added"
|
| 510 |
+
if len(current) + 1 > MAX_TICKERS and symbol not in current:
|
| 511 |
msg = f"Reached max of {MAX_TICKERS}"
|
| 512 |
return new_table, msg
|
| 513 |
|
| 514 |
+
# PATCH 2: keep user entries; no validation here
|
| 515 |
def lock_ticker_column(tb: pd.DataFrame):
|
| 516 |
if tb is None or len(tb) == 0:
|
| 517 |
return pd.DataFrame(columns=["ticker", "amount_usd"])
|
| 518 |
+
tickers = [str(x).upper().strip() for x in tb["ticker"].tolist()]
|
| 519 |
amounts = pd.to_numeric(tb["amount_usd"], errors="coerce").fillna(0.0).tolist()
|
|
|
|
|
|
|
| 520 |
amounts = amounts[:len(tickers)] + [0.0] * max(0, len(tickers) - len(amounts))
|
| 521 |
+
return pd.DataFrame({"ticker": tickers[:MAX_TICKERS], "amount_usd": amounts[:MAX_TICKERS]})
|
| 522 |
|
| 523 |
def set_horizon(years: float):
|
| 524 |
y = max(1.0, min(100.0, float(years)))
|
|
|
|
| 541 |
if len(symbols) == 0:
|
| 542 |
return None, "Add at least one ticker", "Universe empty", empty_positions_df(), empty_suggest_df(), None
|
| 543 |
|
| 544 |
+
# Validation happens HERE (may hit network). If none validate, we'll message the user.
|
| 545 |
symbols = validate_tickers(symbols, years_lookback)
|
| 546 |
if len(symbols) == 0:
|
| 547 |
+
return None, "Could not validate any tickers (check symbols or network)", "Universe invalid", empty_positions_df(), empty_suggest_df(), None
|
| 548 |
|
| 549 |
global UNIVERSE
|
| 550 |
UNIVERSE = list(sorted(set([s for s in symbols if s != MARKET_TICKER] + [MARKET_TICKER])))[:MAX_TICKERS]
|