V3.0: Add rate-limit resilience (cache+retry) for HF Spaces
Browse files
app.py
CHANGED
|
@@ -7,7 +7,7 @@ Risk Engine, Sentiment, Macro, Research Desk, Technical Analysis.
|
|
| 7 |
Bloomberg Terminal aesthetic: black + orange + green + cyan.
|
| 8 |
Powered by K2 Think V2 (MBZUAI) for AI analysis.
|
| 9 |
"""
|
| 10 |
-
import os, json, warnings, math, random
|
| 11 |
from datetime import datetime, timedelta
|
| 12 |
warnings.filterwarnings('ignore')
|
| 13 |
|
|
@@ -56,7 +56,7 @@ class K2ThinkClient:
|
|
| 56 |
return f"🔴 Error: {str(e)[:300]}"
|
| 57 |
|
| 58 |
# =============================================================================
|
| 59 |
-
# MARKET DATA
|
| 60 |
# =============================================================================
|
| 61 |
MARKETS = {
|
| 62 |
"US Equities": {"suffix": "", "ex": "AAPL, TSLA, NVDA, SPY, QQQ"},
|
|
@@ -72,15 +72,41 @@ MARKETS = {
|
|
| 72 |
"Indices": {"suffix": "", "ex": "^GSPC, ^DJI, ^IXIC, ^FTSE"},
|
| 73 |
}
|
| 74 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
def fetch(ticker, period="1y", interval="1d"):
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
|
| 85 |
# =============================================================================
|
| 86 |
# TECHNICAL INDICATORS
|
|
@@ -538,7 +564,8 @@ def crypto_arbitrage(coins):
|
|
| 538 |
if not coin: continue
|
| 539 |
sym = f"{coin}-USD"
|
| 540 |
try:
|
| 541 |
-
|
|
|
|
| 542 |
if not df.empty:
|
| 543 |
results.append({
|
| 544 |
'Coin': coin,
|
|
@@ -549,11 +576,12 @@ def crypto_arbitrage(coins):
|
|
| 549 |
'Volume': f"{df['Volume'].sum():,.0f}",
|
| 550 |
'Spread %': f"{((df['High'].iloc[-1]/df['Low'].iloc[-1]-1)*100):.3f}%"
|
| 551 |
})
|
| 552 |
-
except:
|
| 553 |
-
|
|
|
|
| 554 |
|
| 555 |
if not results:
|
| 556 |
-
return None, "Could not fetch crypto data. Try BTC, ETH, SOL."
|
| 557 |
|
| 558 |
df = pd.DataFrame(results)
|
| 559 |
# Arbitrage heatmap (simulated cross-exchange spreads)
|
|
@@ -736,15 +764,13 @@ def sentiment_analyzer(ticker):
|
|
| 736 |
def macro_analysis():
|
| 737 |
macros = {}
|
| 738 |
for t, name in [('^GSPC','S&P 500'),('^IXIC','Nasdaq'),('^TNX','10Y Treasury'),('GC=F','Gold'),('CL=F','Oil'),('EURUSD=X','EUR/USD'),('DX-Y.NYB','DXY Dollar'),('BTC-USD','Bitcoin')]:
|
| 739 |
-
|
| 740 |
-
|
| 741 |
-
|
| 742 |
-
|
| 743 |
-
'3m': (df['Close'].iloc[-1]/df['Close'].iloc[max(0,len(df)-63)]-1)*100 if len(df)>63 else 0}
|
| 744 |
-
except: pass
|
| 745 |
|
| 746 |
if not macros:
|
| 747 |
-
return "Could not fetch macro data."
|
| 748 |
|
| 749 |
fig = go.Figure()
|
| 750 |
names = list(macros.keys())
|
|
@@ -1113,7 +1139,7 @@ def build_app():
|
|
| 1113 |
### 6. RISK ENGINE
|
| 1114 |
| Feature | Jane Street Practice |
|
| 1115 |
|---------|---------------------|
|
| 1116 |
-
| Parametric + Historical VaR | Dual methodology for compliance |
|
| 1117 |
| Stress testing | 2008, COVID-2020, 2022 rate hike scenarios |
|
| 1118 |
| Correlation breakdown | Crisis: correlations -> 1 |
|
| 1119 |
| Student-t tails | Fat-tail distribution modeling |
|
|
@@ -1121,17 +1147,17 @@ def build_app():
|
|
| 1121 |
### 7. SENTIMENT ANALYZER
|
| 1122 |
| Feature | Jane Street Practice |
|
| 1123 |
|---------|---------------------|
|
| 1124 |
-
| Multi-source NLP | Bloomberg, SEC filings, Twitter, Reddit |
|
| 1125 |
-
|
|
| 1126 |
-
| Temporal
|
| 1127 |
| Alpha factor | Sentiment surprise IC = 0.3-0.5 |
|
| 1128 |
|
| 1129 |
### 8. MACRO DASHBOARD
|
| 1130 |
| Feature | Jane Street Practice |
|
| 1131 |
|---------|---------------------|
|
| 1132 |
| Growth/Inflation quadrant | Bridgewater All Weather framework |
|
| 1133 |
-
| Dollar regime | DXY >100 = risk-off, EM stress |
|
| 1134 |
-
| Yield curve | 10Y-2Y inversion = recession (9/10) |
|
| 1135 |
| Cross-asset momentum | Asness value/momentum factors |
|
| 1136 |
|
| 1137 |
### Stack
|
|
|
|
| 7 |
Bloomberg Terminal aesthetic: black + orange + green + cyan.
|
| 8 |
Powered by K2 Think V2 (MBZUAI) for AI analysis.
|
| 9 |
"""
|
| 10 |
+
import os, json, warnings, math, random, time, hashlib, threading
|
| 11 |
from datetime import datetime, timedelta
|
| 12 |
warnings.filterwarnings('ignore')
|
| 13 |
|
|
|
|
| 56 |
return f"🔴 Error: {str(e)[:300]}"
|
| 57 |
|
| 58 |
# =============================================================================
|
| 59 |
+
# MARKET DATA (with caching + retry to handle HF Spaces shared-IP rate limits)
|
| 60 |
# =============================================================================
|
| 61 |
MARKETS = {
|
| 62 |
"US Equities": {"suffix": "", "ex": "AAPL, TSLA, NVDA, SPY, QQQ"},
|
|
|
|
| 72 |
"Indices": {"suffix": "", "ex": "^GSPC, ^DJI, ^IXIC, ^FTSE"},
|
| 73 |
}
|
| 74 |
|
| 75 |
+
# In-memory cache with TTL for yfinance data (mitigates HF Spaces shared-IP rate limits)
|
| 76 |
+
_FETCH_CACHE = {}
|
| 77 |
+
_FETCH_LOCK = threading.Lock()
|
| 78 |
+
|
| 79 |
+
def _cache_key(ticker, period, interval):
|
| 80 |
+
import hashlib
|
| 81 |
+
return hashlib.md5(f"{ticker.upper().strip()}|{period}|{interval}".encode()).hexdigest()
|
| 82 |
+
|
| 83 |
def fetch(ticker, period="1y", interval="1d"):
|
| 84 |
+
key = _cache_key(ticker, period, interval)
|
| 85 |
+
with _FETCH_LOCK:
|
| 86 |
+
if key in _FETCH_CACHE:
|
| 87 |
+
entry = _FETCH_CACHE[key]
|
| 88 |
+
if time.time() - entry['ts'] < 60:
|
| 89 |
+
return entry['data'], entry['info']
|
| 90 |
+
|
| 91 |
+
t = ticker.upper().strip()
|
| 92 |
+
last_err = ""
|
| 93 |
+
for attempt in range(3):
|
| 94 |
+
try:
|
| 95 |
+
time.sleep(attempt * 1.5)
|
| 96 |
+
stock = yf.Ticker(t)
|
| 97 |
+
df = stock.history(period=period, interval=interval, auto_adjust=False)
|
| 98 |
+
if df.empty:
|
| 99 |
+
return None, f"No data for '{ticker}'."
|
| 100 |
+
info = stock.info if hasattr(stock, 'info') else {}
|
| 101 |
+
with _FETCH_LOCK:
|
| 102 |
+
_FETCH_CACHE[key] = {'ts': time.time(), 'data': df.copy(), 'info': info}
|
| 103 |
+
return df, info
|
| 104 |
+
except Exception as e:
|
| 105 |
+
last_err = str(e)
|
| 106 |
+
if 'Too Many Requests' in last_err or 'Rate limited' in last_err:
|
| 107 |
+
continue
|
| 108 |
+
break
|
| 109 |
+
return None, f"Error fetching '{ticker}': {last_err[:200]}. Yahoo Finance rate-limits shared IPs (HF Spaces). Try again in 30s."
|
| 110 |
|
| 111 |
# =============================================================================
|
| 112 |
# TECHNICAL INDICATORS
|
|
|
|
| 564 |
if not coin: continue
|
| 565 |
sym = f"{coin}-USD"
|
| 566 |
try:
|
| 567 |
+
time.sleep(0.5)
|
| 568 |
+
df = yf.Ticker(sym).history(period="1d", interval="1m", auto_adjust=False)
|
| 569 |
if not df.empty:
|
| 570 |
results.append({
|
| 571 |
'Coin': coin,
|
|
|
|
| 576 |
'Volume': f"{df['Volume'].sum():,.0f}",
|
| 577 |
'Spread %': f"{((df['High'].iloc[-1]/df['Low'].iloc[-1]-1)*100):.3f}%"
|
| 578 |
})
|
| 579 |
+
except Exception as e:
|
| 580 |
+
if 'Rate' not in str(e) and 'Too Many' not in str(e):
|
| 581 |
+
pass # ignore other errors
|
| 582 |
|
| 583 |
if not results:
|
| 584 |
+
return None, "Could not fetch crypto data. Yahoo Finance may be rate-limiting. Try BTC, ETH, SOL, or wait 30s."
|
| 585 |
|
| 586 |
df = pd.DataFrame(results)
|
| 587 |
# Arbitrage heatmap (simulated cross-exchange spreads)
|
|
|
|
| 764 |
def macro_analysis():
|
| 765 |
macros = {}
|
| 766 |
for t, name in [('^GSPC','S&P 500'),('^IXIC','Nasdaq'),('^TNX','10Y Treasury'),('GC=F','Gold'),('CL=F','Oil'),('EURUSD=X','EUR/USD'),('DX-Y.NYB','DXY Dollar'),('BTC-USD','Bitcoin')]:
|
| 767 |
+
df, info, err = fetch(t, "3mo")
|
| 768 |
+
if df is not None and not df.empty:
|
| 769 |
+
macros[name] = {'price': df['Close'].iloc[-1], '1m': (df['Close'].iloc[-1]/df['Close'].iloc[0]-1)*100,
|
| 770 |
+
'3m': (df['Close'].iloc[-1]/df['Close'].iloc[max(0,len(df)-63)]-1)*100 if len(df)>63 else 0}
|
|
|
|
|
|
|
| 771 |
|
| 772 |
if not macros:
|
| 773 |
+
return None, "Could not fetch macro data."
|
| 774 |
|
| 775 |
fig = go.Figure()
|
| 776 |
names = list(macros.keys())
|
|
|
|
| 1139 |
### 6. RISK ENGINE
|
| 1140 |
| Feature | Jane Street Practice |
|
| 1141 |
|---------|---------------------|
|
| 1142 |
+
| Parametric + Historical VaR | Dual methodology for regulatory compliance |
|
| 1143 |
| Stress testing | 2008, COVID-2020, 2022 rate hike scenarios |
|
| 1144 |
| Correlation breakdown | Crisis: correlations -> 1 |
|
| 1145 |
| Student-t tails | Fat-tail distribution modeling |
|
|
|
|
| 1147 |
### 7. SENTIMENT ANALYZER
|
| 1148 |
| Feature | Jane Street Practice |
|
| 1149 |
|---------|---------------------|
|
| 1150 |
+
| Multi-source NLP pipeline | Bloomberg headlines, SEC filings, Twitter, Reddit |
|
| 1151 |
+
| Named Entity Recognition | Company/executive/product mention extraction |
|
| 1152 |
+
| Temporal analysis | Improving vs deteriorating sentiment |
|
| 1153 |
| Alpha factor | Sentiment surprise IC = 0.3-0.5 |
|
| 1154 |
|
| 1155 |
### 8. MACRO DASHBOARD
|
| 1156 |
| Feature | Jane Street Practice |
|
| 1157 |
|---------|---------------------|
|
| 1158 |
| Growth/Inflation quadrant | Bridgewater All Weather framework |
|
| 1159 |
+
| Dollar regime | DXY > 100 = risk-off, EM stress |
|
| 1160 |
+
| Yield curve | 10Y-2Y inversion = recession (9/10 accuracy) |
|
| 1161 |
| Cross-asset momentum | Asness value/momentum factors |
|
| 1162 |
|
| 1163 |
### Stack
|