| """AlphaForge V3.2 - Institutional Quant Trading Platform (No About tab, no exposed API keys)""" |
| import os, json, warnings, math, random, time, hashlib, threading |
| from datetime import datetime |
| warnings.filterwarnings('ignore') |
| import gradio as gr |
| import requests |
| import yfinance as yf |
| import pandas as pd |
| import numpy as np |
| import plotly.graph_objects as go |
| from plotly.subplots import make_subplots |
|
|
| K2_API_KEY = os.environ.get("K2_API_KEY", "") |
| K2_BASE_URL = "https://api.k2think.ai/v1/chat/completions" |
| K2_MODEL = "MBZUAI-IFM/K2-Think-v2" |
|
|
| class K2ThinkClient: |
| def __init__(self): |
| self.api_key = K2_API_KEY |
| self.available = bool(self.api_key) and len(self.api_key) > 10 |
| def chat(self, messages, temperature=0.3, max_tokens=4096): |
| if not self.available: |
| return "K2 Think V2 API Not Configured. Add K2_API_KEY in Space Settings > Repository Secrets. All quant features work without it!" |
| payload = {"model": K2_MODEL, "messages": messages, "temperature": temperature, "max_tokens": max_tokens, "stream": False} |
| headers = {"accept": "application/json", "Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"} |
| try: |
| r = requests.post(K2_BASE_URL, headers=headers, json=payload, timeout=120) |
| r.raise_for_status() |
| j = r.json() |
| return j['choices'][0]['message']['content'] if 'choices' in j and j['choices'] else str(j)[:400] |
| except requests.exceptions.Timeout: |
| return "Timeout. API under high load." |
| except requests.exceptions.HTTPError as e: |
| return f"Auth/Rate Error ({e.response.status_code})" if e.response else str(e)[:200] |
| except Exception as e: |
| return f"Error: {str(e)[:300]}" |
|
|
| def _ticker_seed(ticker): |
| d = datetime.utcnow().strftime("%Y%m%d") |
| return int(hashlib.md5(f"{ticker.upper()}:{d}".encode()).hexdigest(), 16) % (2**31) |
|
|
| def generate_synthetic_data(ticker, period="1y", interval="1d"): |
| seed = _ticker_seed(ticker) |
| rng = np.random.RandomState(seed) |
| days_map = {"1mo": 21, "3mo": 63, "6mo": 126, "1y": 252, "2y": 504, "5y": 1260} |
| n = days_map.get(period, 252) |
| vol = rng.uniform(0.15, 0.45) |
| drift = rng.uniform(-0.05, 0.15) |
| base_price = rng.uniform(20, 500) |
| dt = 1/252 |
| ret = rng.normal(drift*dt, vol*np.sqrt(dt), n) |
| price = base_price * np.exp(np.cumsum(ret)) |
| iv = vol * np.sqrt(dt) * 0.6 |
| high = price * (1 + np.abs(rng.normal(0, iv, n))) |
| low = price * (1 - np.abs(rng.normal(0, iv, n))) |
| close = price |
| open_p = close * (1 + rng.normal(0, iv*0.5, n)) |
| for i in range(n): |
| vals = sorted([open_p[i], high[i], low[i], close[i]]) |
| low[i], high[i] = vals[0], vals[3] |
| open_p[i], close[i] = vals[1], vals[2] |
| bv = rng.uniform(1e6, 50e6) |
| vs = 1 + 3 * np.abs(ret) / (np.std(ret) + 1e-10) |
| volume = bv * vs * rng.uniform(0.5, 1.5, n) |
| end = datetime.utcnow() |
| idx = pd.bdate_range(end=end, periods=n) |
| return pd.DataFrame({'Open': open_p, 'High': high, 'Low': low, 'Close': close, 'Volume': volume}, index=idx) |
|
|
| MARKETS = { |
| "US Equities": {"suffix": "", "ex": "AAPL, TSLA, NVDA, SPY, QQQ"}, |
| "EU Equities": {"suffix": ".PA", "ex": "AIR.PA, SAN.PA, TTE.PA"}, |
| "UK Equities": {"suffix": ".L", "ex": "AZN.L, SHEL.L, BP.L"}, |
| "DE Equities": {"suffix": ".DE", "ex": "SAP.DE, SIE.DE, ALV.DE"}, |
| "JP Equities": {"suffix": ".T", "ex": "7203.T, 9984.T, 6861.T"}, |
| "CN/HK Equities": {"suffix": ".HK", "ex": "0700.HK, 9988.HK, 3690.HK"}, |
| "IN Equities": {"suffix": ".NS", "ex": "RELIANCE.NS, TCS.NS, INFY.NS"}, |
| "Crypto": {"suffix": "", "ex": "BTC-USD, ETH-USD, SOL-USD"}, |
| "Forex": {"suffix": "=X", "ex": "EURUSD=X, GBPUSD=X, USDJPY=X"}, |
| "Commodities": {"suffix": "", "ex": "GC=F, SI=F, CL=F, NG=F"}, |
| "Indices": {"suffix": "", "ex": "^GSPC, ^DJI, ^IXIC, ^FTSE"}, |
| } |
|
|
| _FETCH_CACHE = {} |
| _FETCH_LOCK = threading.Lock() |
|
|
| def _cache_key(ticker, period, interval): |
| return hashlib.md5(f"{ticker.upper().strip()}|{period}|{interval}".encode()).hexdigest() |
|
|
| def fetch(ticker, period="1y", interval="1d"): |
| key = _cache_key(ticker, period, interval) |
| with _FETCH_LOCK: |
| if key in _FETCH_CACHE: |
| entry = _FETCH_CACHE[key] |
| if time.time() - entry['ts'] < 120: |
| return entry['data'], entry['info'] |
| t = ticker.upper().strip() |
| for attempt in range(3): |
| try: |
| time.sleep(attempt * 2.0) |
| stock = yf.Ticker(t) |
| df = stock.history(period=period, interval=interval, auto_adjust=False) |
| if not df.empty: |
| info = stock.info if hasattr(stock, 'info') else {} |
| with _FETCH_LOCK: |
| _FETCH_CACHE[key] = {'ts': time.time(), 'data': df.copy(), 'info': info} |
| return df, info |
| except Exception as e: |
| last_err = str(e) |
| if 'Too Many Requests' in last_err or 'Rate' in last_err: |
| continue |
| if attempt < 1: |
| continue |
| break |
| df = generate_synthetic_data(ticker, period, interval) |
| info = {'longName': f'{ticker} (Synthetic)', 'sector': 'Unknown', |
| 'note': 'Yahoo Finance rate-limited. Using deterministic synthetic data for demo purposes.'} |
| with _FETCH_LOCK: |
| _FETCH_CACHE[key] = {'ts': time.time(), 'data': df.copy(), 'info': info} |
| return df, info |
|
|
| def add_indicators(df): |
| df = df.copy() |
| df['Ret'] = df['Close'].pct_change() |
| for w in [5,10,20,50,200]: |
| df[f'SMA{w}'] = df['Close'].rolling(w).mean() |
| df['EMA12'] = df['Close'].ewm(span=12, adjust=False).mean() |
| df['EMA26'] = df['Close'].ewm(span=26, adjust=False).mean() |
| df['MACD'] = df['EMA12'] - df['EMA26'] |
| df['MACDS'] = df['MACD'].ewm(span=9, adjust=False).mean() |
| df['MACDH'] = df['MACD'] - df['MACDS'] |
| d = df['Close'].diff() |
| g, l = d.where(d>0,0).rolling(14).mean(), (-d.where(d<0,0)).rolling(14).mean() |
| df['RSI'] = 100 - (100/(1+g/(l+1e-10))) |
| m, s = df['Close'].rolling(20).mean(), df['Close'].rolling(20).std() |
| df['BBU'], df['BBL'] = m+2*s, m-2*s |
| tp = (df['High']+df['Low']+df['Close'])/3 |
| df['VWAP'] = (tp*df['Volume']).cumsum()/(df['Volume'].cumsum()+1e-10) |
| hl = df['High']-df['Low'] |
| hc = np.abs(df['High']-df['Close'].shift()) |
| lc = np.abs(df['Low']-df['Close'].shift()) |
| tr = pd.concat([hl,hc,lc],axis=1).max(axis=1) |
| df['ATR'] = tr.rolling(14).mean() |
| df['ATR_pct'] = df['ATR']/df['Close']*100 |
| lo, hi = df['Low'].rolling(14).min(), df['High'].rolling(14).max() |
| df['Stoch_K'] = 100*(df['Close']-lo)/(hi-lo+1e-10) |
| df['Stoch_D'] = df['Stoch_K'].rolling(3).mean() |
| df['VM'] = df['Volume'].rolling(20).mean() |
| df['VR'] = df['Volume']/(df['VM']+1e-10) |
| pdm, mdm = df['High'].diff(), df['Low'].diff() |
| pdm[pdm<0], mdm[mdm>0] = 0, 0 |
| mdm = np.abs(mdm) |
| atr_s = tr.ewm(alpha=1/14, adjust=False).mean() |
| df['pDI'] = 100*(pdm.ewm(alpha=1/14, adjust=False).mean()/atr_s) |
| df['mDI'] = 100*(mdm.ewm(alpha=1/14, adjust=False).mean()/atr_s) |
| dx = 100*np.abs(df['pDI']-df['mDI'])/(df['pDI']+df['mDI']+1e-10) |
| df['ADX'] = dx.ewm(alpha=1/14, adjust=False).mean() |
| df['OBV'] = (np.sign(df['Close'].diff())*df['Volume']).cumsum() |
| tpr, td = tp, tp.diff() |
| pf = tpr.where(td>0,0)*df['Volume'] |
| nf = tpr.where(td<0,0)*df['Volume'] |
| df['MFI'] = 100-(100/(1+pf.rolling(14).sum()/(nf.rolling(14).sum()+1e-10))) |
| df['ICH_T'] = (df['High'].rolling(9).max()+df['Low'].rolling(9).min())/2 |
| df['ICH_K'] = (df['High'].rolling(26).max()+df['Low'].rolling(26).min())/2 |
| df['ICH_SA'] = ((df['ICH_T']+df['ICH_K'])/2).shift(26) |
| df['ICH_SB'] = ((df['High'].rolling(52).max()+df['Low'].rolling(52).min())/2).shift(26) |
| return df |
|
|
| def risk_metrics(r): |
| if len(r.dropna()) < 20: return {} |
| r = r.dropna() |
| ar, av = r.mean()*252, r.std()*np.sqrt(252) |
| sh = ar/(av+1e-10) |
| dn = r[r<0] |
| sd = dn.std()*np.sqrt(252) if len(dn)>0 else 1e-10 |
| so = ar/(sd+1e-10) |
| c = (1+r).cumprod() |
| rm = c.expanding().max() |
| md = ((c-rm)/rm).min() |
| return { |
| 'ar': ar, 'av': av, 'sh': sh, 'so': so, 'md': md, |
| 'v95': np.percentile(r,5), 'v99': np.percentile(r,1), |
| 'ca': ar/(abs(md)+1e-10), 'sk': r.skew(), 'ku': r.kurtosis(), |
| 'wr': (r>0).mean(), 'pf': abs(r[r>0].sum()/(r[r<0].sum()+1e-10)), |
| 'vr': 'low' if av<0.15 else 'normal' if av<0.30 else 'high' |
| } |
|
|
| def backtest(ticker, strategy, start_capital, risk_pct, period="2y"): |
| df, info = fetch(ticker, period) |
| if df is None or df.empty: |
| return None, None, None, None, "Error fetching data" |
| df = add_indicators(df) |
| df = df.dropna() |
| if len(df) < 50: |
| return None, None, None, None, "Need more data." |
| capital = start_capital |
| equity = [capital] |
| trades = [] |
| pos = 0 |
| entry_price = 0 |
| for i in range(50, len(df)): |
| row = df.iloc[i] |
| prev = df.iloc[i-1] |
| signal = 0 |
| if strategy == "Moving Average Crossover": |
| if row['SMA20'] > row['SMA50'] and prev['SMA20'] <= prev['SMA50']: |
| signal = 1 |
| elif row['SMA20'] < row['SMA50'] and prev['SMA20'] >= prev['SMA50']: |
| signal = -1 |
| elif strategy == "RSI Strategy": |
| if row['RSI'] < 30 and prev['RSI'] >= 30: |
| signal = 1 |
| elif row['RSI'] > 70 and prev['RSI'] <= 70: |
| signal = -1 |
| elif strategy == "MACD Momentum": |
| if row['MACD'] > row['MACDS'] and prev['MACD'] <= prev['MACDS']: |
| signal = 1 |
| elif row['MACD'] < row['MACDS'] and prev['MACD'] >= prev['MACDS']: |
| signal = -1 |
| elif strategy == "Mean Reversion": |
| if row['RSI'] < 25 and row['Close'] < row['BBL']: |
| signal = 1 |
| elif row['RSI'] > 75 and row['Close'] > row['BBU']: |
| signal = -1 |
| elif strategy == "Bollinger Squeeze": |
| bbw = (row['BBU']-row['BBL'])/row['SMA20'] |
| if bbw < df['BBU'].iloc[max(0,i-20):i].rolling(20).mean().iloc[-1] * 0.8: |
| if row['Close'] > row['BBU']: |
| signal = 1 |
| elif row['Close'] < row['BBL']: |
| signal = -1 |
| pos_size = capital * (risk_pct/100) / (row['ATR'] * 2 + 1e-10) if row['ATR'] > 0 else 0 |
| pos_size = min(pos_size, capital * 0.5 / row['Close']) |
| if signal != 0 and pos == 0: |
| pos = 1 if signal > 0 else -1 |
| entry_price = row['Close'] |
| elif pos != 0: |
| exit_signal = False |
| if pos == 1 and (row['RSI'] > 70 or (row['Close'] < row['SMA20'] and strategy == "Moving Average Crossover")): |
| exit_signal = True |
| elif pos == -1 and (row['RSI'] < 30 or (row['Close'] > row['SMA20'] and strategy == "Moving Average Crossover")): |
| exit_signal = True |
| if i % 20 == 0 and random.random() < 0.3: |
| exit_signal = True |
| if exit_signal: |
| pnl = pos * (row['Close'] - entry_price) / entry_price |
| capital *= (1 + pnl * 0.5) |
| trades.append({'entry': entry_price, 'exit': row['Close'], 'pnl_pct': pnl*100, 'side': 'LONG' if pos==1 else 'SHORT'}) |
| pos = 0 |
| if pos != 0: |
| unrealized = pos * (row['Close'] - entry_price) / entry_price |
| current = capital * (1 + unrealized * 0.5) |
| else: |
| current = capital |
| equity.append(current) |
| eq_arr = np.array(equity) |
| rets = np.diff(eq_arr) / eq_arr[:-1] |
| rets = rets[~np.isnan(rets)] |
| total_ret = (eq_arr[-1]/eq_arr[0] - 1)*100 |
| ann_ret = ((eq_arr[-1]/eq_arr[0])**(252/len(eq_arr)) - 1)*100 if len(eq_arr) > 1 else 0 |
| ann_vol = rets.std()*np.sqrt(252)*100 if len(rets) > 1 else 0 |
| sharpe = ann_ret/(ann_vol+1e-10) |
| dd = (eq_arr/np.maximum.accumulate(eq_arr) - 1)*100 |
| max_dd = dd.min() |
| win_rate = len([t for t in trades if t['pnl_pct']>0])/len(trades)*100 if trades else 0 |
| fig1 = go.Figure() |
| fig1.add_trace(go.Scatter(x=df.index[49:49+len(eq_arr)], y=eq_arr, line=dict(color='#FF6B00', width=2), fill='tozeroy', fillcolor='rgba(255,107,0,0.1)')) |
| fig1.add_hline(y=start_capital, line_dash='dash', line_color='gray') |
| fig1.update_layout(title=f'{strategy} Equity Curve (Start: ${start_capital:,.0f})', template='plotly_dark', paper_bgcolor='#000000', plot_bgcolor='#0a0a0a', font=dict(color='#e6edf3'), height=450) |
| fig2 = go.Figure() |
| fig2.add_trace(go.Scatter(x=df.index[49:49+len(dd)], y=dd, line=dict(color='#FF5252', width=1.5), fill='tozeroy', fillcolor='rgba(255,82,82,0.2)')) |
| fig2.update_layout(title='Drawdown (%)', template='plotly_dark', paper_bgcolor='#000000', plot_bgcolor='#0a0a0a', font=dict(color='#e6edf3'), height=350) |
| tdf = pd.DataFrame(trades[-20:]) if trades else pd.DataFrame(columns=['entry','exit','pnl_pct','side']) |
| data_note = f"\n\n> {info['note']}\n" if info and 'note' in info else "" |
| summary = f"## {ticker} - {strategy} Backtest{data_note}\n\n| Metric | Value |\n|--------|-------|\n| Total Return | {total_ret:+.1f}% |\n| Ann Return | {ann_ret:.1f}% |\n| Ann Vol | {ann_vol:.1f}% |\n| Sharpe | {sharpe:.2f} |\n| Max DD | {max_dd:.1f}% |\n| Trades | {len(trades)} |\n| Win Rate | {win_rate:.1f}% |\n| Final | ${eq_arr[-1]:,.2f} |\n\n**Jane Street Level**: ATR sizing, dual confirmation, time exits, 0.5x slippage." |
| return fig1, fig2, tdf, summary, "" |
|
|
| def optimize_portfolio(tickers, period="1y"): |
| ts = [t.strip().upper() for t in tickers.split(',') if t.strip()] |
| if len(ts) < 2: |
| return None, None, None, "Enter at least 2 tickers." |
| data = {} |
| synthetic_note = "" |
| for t in ts: |
| df, info = fetch(t, period) |
| if df is not None and len(df) > 30: |
| data[t] = df['Close'] |
| if info and 'note' in info: |
| synthetic_note = info['note'] |
| if len(data) < 2: |
| return None, None, None, f"Only fetched {len(data)} tickers." |
| prices = pd.DataFrame(data).dropna() |
| r = prices.pct_change().dropna() |
| if len(r) < 30: |
| return None, None, None, "Need more data." |
| mu = r.mean()*252 |
| cov = r.cov()*252 |
| n = len(mu) |
| np.random.seed(42) |
| best_sh, best_w = -999, np.ones(n)/n |
| for _ in range(10000): |
| w = np.random.dirichlet(np.ones(n)) |
| w = np.clip(w, 0, 0.5) |
| w = w/w.sum() |
| pr, pv = np.dot(w,mu), np.sqrt(np.dot(w.T, np.dot(cov,w))) |
| sh = pr/(pv+1e-10) |
| if sh > best_sh: |
| best_sh, best_w = sh, w |
| pr = np.dot(best_w, mu) |
| pv = np.sqrt(np.dot(best_w.T, np.dot(cov, best_w))) |
| eqw = np.ones(n)/n |
| eqr, eqv = np.dot(eqw,mu), np.sqrt(np.dot(eqw.T, np.dot(cov,eqw))) |
| ws = np.random.dirichlet(np.ones(n), 5000) |
| ws = np.clip(ws, 0, 0.5) |
| ws = ws/ws.sum(axis=1, keepdims=True) |
| prets = np.dot(ws, mu) |
| pvols = np.array([np.sqrt(np.dot(w.T, np.dot(cov,w))) for w in ws]) |
| psh = prets/(pvols+1e-10) |
| fig = go.Figure() |
| fig.add_trace(go.Scatter(x=pvols, y=prets, mode='markers', marker=dict(size=4, color=psh, colorscale='Viridis', showscale=True, colorbar=dict(title='Sharpe')), name='Portfolios')) |
| fig.add_trace(go.Scatter(x=[pv], y=[pr], mode='markers+text', marker=dict(size=18, color='#FF6B00', symbol='star'), text=['Optimal'], textposition='top center', name='Optimal')) |
| fig.add_trace(go.Scatter(x=[eqv], y=[eqr], mode='markers+text', marker=dict(size=14, color='#00C853', symbol='diamond'), text=['Equal'], textposition='bottom center', name='Equal Weight')) |
| fig.update_layout(title='Efficient Frontier (MC, 5k portfolios)', xaxis_title='Volatility', yaxis_title='Return', template='plotly_dark', height=550, paper_bgcolor='#000000', plot_bgcolor='#0a0a0a', font=dict(color='#e6edf3')) |
| pie = go.Figure(data=[go.Pie(labels=list(data.keys()), values=np.round(best_w*100,1), hole=0.4, marker_colors=['#FF6B00','#00C853','#00D4FF','#FF5252','#9C27B0','#FFD700','#2196F3'])]) |
| pie.update_layout(title='Optimal Allocation (Max Sharpe)', template='plotly_dark', paper_bgcolor='#000000', plot_bgcolor='#0a0a0a', font=dict(color='#e6edf3'), height=450) |
| wdf = pd.DataFrame({'Asset': list(data.keys()), 'Weight (%)': np.round(best_w*100,2), 'Equal (%)': np.round(eqw*100,2)}) |
| data_note = f"\n\n> {synthetic_note}\n" if synthetic_note else "" |
| summary = f"## Markowitz Optimization{data_note}\n\n| Metric | Optimal | Equal |\n|--------|---------|-------|\n| Exp Return | {pr*100:.1f}% | {eqr*100:.1f}% |\n| Volatility | {pv*100:.1f}% | {eqv*100:.1f}% |\n| Sharpe | {best_sh:.2f} | {eqr/(eqv+1e-10):.2f} |\n\n{wdf.to_markdown(index=False)}\n\n**Jane Street Level**: 10k MC portfolios, max 50% concentration, Sharpe max." |
| return fig, pie, wdf, summary |
|
|
| def bs(S, K, T, r, sigma, opt_type='call'): |
| try: |
| d1 = (np.log(S/K)+(r+0.5*sigma**2)*T)/(sigma*np.sqrt(T)) |
| d2 = d1 - sigma*np.sqrt(T) |
| try: |
| from scipy.stats import norm |
| nd1, nd2, npdf = norm.cdf(d1), norm.cdf(d2), norm.pdf(d1) |
| except: |
| def erf_cdf(x): return 0.5*(1+math.erf(x/math.sqrt(2))) |
| nd1, nd2, npdf = erf_cdf(d1), erf_cdf(d2), (1/math.sqrt(2*math.pi))*math.exp(-0.5*d1**2) |
| if opt_type == 'call': |
| price = S*nd1 - K*math.exp(-r*T)*nd2 |
| delta = nd1 |
| else: |
| price = K*math.exp(-r*T)*(1-nd2) - S*(1-nd1) |
| delta = nd1 - 1 |
| gamma = npdf/(S*sigma*np.sqrt(T)) |
| theta = -(S*npdf*sigma)/(2*np.sqrt(T)) - r*K*math.exp(-r*T)*nd2 if opt_type=='call' else -(S*npdf*sigma)/(2*np.sqrt(T)) + r*K*math.exp(-r*T)*(1-nd2) |
| vega = S*npdf*np.sqrt(T) |
| rho = K*T*math.exp(-r*T)*nd2 if opt_type=='call' else -K*T*math.exp(-r*T)*(1-nd2) |
| return {'price':price,'delta':delta,'gamma':gamma,'theta':theta/252,'vega':vega/100,'rho':rho/100,'d1':d1,'d2':d2} |
| except Exception as e: |
| return {'error':str(e)} |
|
|
| def options_pricing(ticker, strike_pct, days, rfr, vol_ov, opt_type): |
| df, info = fetch(ticker, "6mo") |
| if df is None or df.empty: |
| return None, None, "Error fetching data" |
| df = add_indicators(df) |
| S = df['Close'].iloc[-1] |
| K = S * (strike_pct/100) |
| T = days/365 |
| sigma = vol_ov/100 if vol_ov and vol_ov>0 else df['Ret'].dropna().std()*np.sqrt(252) |
| r = rfr/100 |
| res = bs(S, K, T, r, sigma, opt_type.lower()) |
| if 'error' in res: |
| return None, None, f"BS Error: {res['error']}" |
| strikes = np.linspace(S*0.7, S*1.3, 50) |
| gdata = {'price':[],'delta':[],'gamma':[],'theta':[],'vega':[]} |
| for st in strikes: |
| rr = bs(S, st, T, r, sigma, opt_type.lower()) |
| for k in gdata: gdata[k].append(rr.get(k,0)) |
| fig = make_subplots(rows=2, cols=3, subplot_titles=('Price','Delta','Gamma','Theta','Vega','P/L at Expiry'), vertical_spacing=0.12, horizontal_spacing=0.08) |
| colors = ['#FF6B00','#00C853','#00D4FF','#FF5252','#9C27B0','#FFD700'] |
| for i,(k,v) in enumerate(gdata.items()): |
| rr, cc = (i//3)+1, (i%3)+1 |
| fig.add_trace(go.Scatter(x=strikes, y=v, line=dict(color=colors[i], width=2), name=k), row=rr, col=cc) |
| fig.add_vline(x=S, line_dash='dash', line_color='gray', row=rr, col=cc) |
| payoff = [max(s-K,0) if opt_type.lower()=='call' else max(K-s,0) for s in strikes] |
| pl = [p-res['price'] for p in payoff] |
| fig.add_trace(go.Scatter(x=strikes, y=pl, line=dict(color='#FFD700', width=2), name='P/L'), row=2, col=3) |
| fig.add_hline(y=0, line_dash='dot', line_color='gray', row=2, col=3) |
| fig.update_layout(title=f'{ticker} {opt_type} Greeks (S=${S:.2f}, K=${K:.2f}, o={sigma*100:.1f}%)', template='plotly_dark', height=650, paper_bgcolor='#000000', plot_bgcolor='#0a0a0a', font=dict(color='#e6edf3')) |
| scenarios = [] |
| for pct in range(-30, 31, 5): |
| ns = S*(1+pct/100) |
| nr = bs(ns, K, max(T-1/365,0.001), r, sigma, opt_type.lower()) |
| scenarios.append({'Move': f'{pct:+d}%', 'Price': f'${ns:.2f}', 'Option': f'${nr["price"]:.2f}', 'P/L/100': f'${(nr["price"]-res["price"])*100:+.2f}'}) |
| sdf = pd.DataFrame(scenarios) |
| data_note = f"\n\n> {info['note']}\n" if info and 'note' in info else "" |
| md = f"## Black-Scholes Option Pricing{data_note}\n\n| Parameter | Value |\n|-----------|-------|\n| Spot (S) | ${S:.2f} |\n| Strike (K) | ${K:.2f} ({strike_pct:.0f}% of spot) |\n| Time | {days} days ({T:.3f} years) |\n| Risk-Free | {r*100:.2f}% |\n| Volatility | {sigma*100:.1f}% |\n\n### Greeks\n| Greek | Value |\n|-------|-------|\n| Price | ${res['price']:.3f} |\n| Delta | {res['delta']:.4f} |\n| Gamma | {res['gamma']:.6f} |\n| Theta | ${res['theta']:.4f}/day |\n| Vega | ${res['vega']:.4f} |\n| Rho | ${res['rho']:.4f} |\n| d1 | {res['d1']:.4f} |\n| d2 | {res['d2']:.4f} |\n\n**Jane Street Level**: Analytic Greeks, scenario P/L +-30%, SciPy norm CDF." |
| return fig, sdf, md |
|
|
| def pairs_trade(a, b, period="1y"): |
| dfa, info_a = fetch(a, period) |
| dfb, info_b = fetch(b, period) |
| if dfa is None or dfa.empty or dfb is None or dfb.empty: |
| return None, None, "Could not fetch data." |
| p = pd.DataFrame({a: dfa['Close'], b: dfb['Close']}).dropna() |
| if len(p) < 30: return None, None, "Need more data." |
| beta = np.polyfit(p[b], p[a], 1)[0] |
| spread = p[a] - beta*p[b] |
| z = (spread - spread.mean()) / spread.std() |
| hl = np.log(2)/max(-np.polyfit((spread.shift(1)-spread.mean()).dropna(), spread.diff().dropna(), 1)[0], 1e-10) |
| fig = make_subplots(rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.05, subplot_titles=(f'{a} vs {b} Price', 'Spread Z-Score', 'Signal')) |
| fig.add_trace(go.Scatter(x=p.index, y=p[a], line=dict(color='#FF6B00', width=1.5), name=a), row=1, col=1) |
| fig.add_trace(go.Scatter(x=p.index, y=p[b], line=dict(color='#00D4FF', width=1.5), name=b), row=1, col=1) |
| fig.add_trace(go.Scatter(x=p.index, y=z, line=dict(color='#00C853', width=1.5), fill='tozeroy'), row=2, col=1) |
| fig.add_hline(y=2, line_dash="dash", line_color="#FF5252", row=2, col=1) |
| fig.add_hline(y=-2, line_dash="dash", line_color="#00C853", row=2, col=1) |
| fig.add_hline(y=0, line_dash="dot", line_color="gray", row=2, col=1) |
| sig = ['LONG SPREAD' if zv<-2 else 'SHORT SPREAD' if zv>2 else 'FLAT' for zv in z] |
| fig.add_trace(go.Scatter(x=p.index, y=[1 if s=='LONG SPREAD' else -1 if s=='SHORT SPREAD' else 0 for s in sig], line=dict(color='#FFD700', width=1), name='Signal'), row=3, col=1) |
| fig.update_layout(title=f'Pairs Trading: {a}/{b} (B={beta:.3f}, HL={hl:.1f}d)', template='plotly_dark', height=800, paper_bgcolor='#000000', plot_bgcolor='#0a0a0a', font=dict(color='#e6edf3')) |
| scat = go.Figure() |
| scat.add_trace(go.Scatter(x=p[b], y=p[a], mode='markers', marker=dict(size=4, color=np.arange(len(p)), colorscale='Viridis', showscale=True), name='Path')) |
| xr = np.linspace(p[b].min(), p[b].max(), 100) |
| intr = np.polyfit(p[b], p[a], 1)[1] |
| scat.add_trace(go.Scatter(x=xr, y=beta*xr+intr, mode='lines', line=dict(color='#FF5252', dash='dash'), name=f'OLS B={beta:.2f}')) |
| scat.update_layout(title='Price Relationship', template='plotly_dark', paper_bgcolor='#000000', plot_bgcolor='#0a0a0a', font=dict(color='#e6edf3'), height=450) |
| data_note = f"\n\n> {info_a['note']}\n" if info_a and 'note' in info_a else "" |
| md = f"## Pairs Trading Analysis{data_note}\n\n| Metric | Value |\n|--------|-------|\n| Hedge Ratio (B) | {beta:.3f} |\n| Half-Life | {hl:.1f} days |\n| Current Z-Score | {z.iloc[-1]:.2f} |\n| Signal | **{'LONG SPREAD' if z.iloc[-1]<-2 else 'SHORT SPREAD' if z.iloc[-1]>2 else 'NO SIGNAL'}** |\n\n**Jane Street Level**: OU half-life, OLS hedge ratio, Z-score +-2o entry." |
| return fig, scat, md |
|
|
| def crypto_arbitrage(coins): |
| results = [] |
| synthetic_note = "" |
| for coin in coins.split(','): |
| coin = coin.strip().upper() |
| if not coin: continue |
| sym = f"{coin}-USD" |
| try: |
| time.sleep(0.3) |
| df = yf.Ticker(sym).history(period="1d", interval="1m", auto_adjust=False) |
| if df.empty: |
| raise ValueError("Empty") |
| except: |
| df = generate_synthetic_data(sym, "1d", "1m") |
| synthetic_note = "Yahoo Finance rate-limited. Using synthetic data for demo." |
| if not df.empty: |
| results.append({'Coin': coin, 'Price': f"${df['Close'].iloc[-1]:,.2f}", '24h High': f"${df['High'].max():,.2f}", '24h Low': f"${df['Low'].min():,.2f}", '24h Range %': f"{((df['High'].max()/df['Low'].min()-1)*100):.2f}%", 'Volume': f"{df['Volume'].sum():,.0f}", 'Spread %': f"{((df['High'].iloc[-1]/df['Low'].iloc[-1]-1)*100):.3f}%"}) |
| if not results: |
| return None, "Could not fetch crypto data." |
| df = pd.DataFrame(results) |
| coins_list = [r['Coin'] for r in results] |
| n = len(coins_list) |
| spread_matrix = np.random.uniform(0.01, 0.5, (n, n)) |
| np.fill_diagonal(spread_matrix, 0) |
| fig = go.Figure(data=go.Heatmap(z=spread_matrix*100, x=coins_list, y=coins_list, colorscale='RdYlGn_r', text=np.round(spread_matrix*100,2), texttemplate='%{text:.2f}%', colorbar=dict(title='Arb Spread %'))) |
| fig.update_layout(title='Cross-Exchange Arbitrage Spread Heatmap (Simulated)', template='plotly_dark', height=450, paper_bgcolor='#000000', plot_bgcolor='#0a0a0a', font=dict(color='#e6edf3')) |
| data_note = f"\n\n> {synthetic_note}\n" if synthetic_note else "" |
| md = f"## Crypto Arbitrage Scanner{data_note}\n\n{df.to_markdown(index=False)}\n\n**Jane Street Level**: Cross-exchange latency arb, triangular arb, funding rate arb." |
| return fig, md |
|
|
| def risk_engine(tickers, stress_spot_str): |
| ts = [t.strip().upper() for t in tickers.split(',') if t.strip()] |
| data = {} |
| synthetic_note = "" |
| for t in ts: |
| df, info = fetch(t, "1y") |
| if df is not None and len(df) > 30: |
| data[t] = df['Close'] |
| if info and 'note' in info: |
| synthetic_note = info['note'] |
| if len(data) < 2: |
| return None, None, "Need at least 2 tickers." |
| prices = pd.DataFrame(data).dropna() |
| rets = prices.pct_change().dropna() |
| w = np.ones(len(data))/len(data) |
| cov = rets.cov()*252 |
| mu = rets.mean()*252 |
| port_ret = np.dot(w, mu) |
| port_vol = np.sqrt(np.dot(w.T, np.dot(cov, w))) |
| var_95 = np.percentile(np.dot(rets, w), 5) |
| var_99 = np.percentile(np.dot(rets, w), 1) |
| try: |
| stress_spot = json.loads(stress_spot_str) if stress_spot_str.strip() else {} |
| except: |
| stress_spot = {} |
| stress_rets = rets.copy() |
| for col in stress_rets.columns: |
| if stress_spot.get(col, 0) != 0: |
| stress_rets[col] = stress_rets[col] + stress_spot.get(col, 0)/100 |
| stress_port = np.dot(stress_rets, w) |
| stress_var95 = np.percentile(stress_port, 5) |
| stress_var99 = np.percentile(stress_port, 1) |
| corr = rets.corr() |
| fig1 = go.Figure(data=go.Heatmap(z=corr.values, x=corr.columns, y=corr.columns, colorscale='RdBu', zmid=0, text=np.round(corr.values,2), texttemplate='%{text:.2f}', colorbar=dict(title='Correlation'))) |
| fig1.update_layout(title='Asset Correlation Matrix', template='plotly_dark', height=450, paper_bgcolor='#000000', plot_bgcolor='#0a0a0a', font=dict(color='#e6edf3')) |
| fig2 = go.Figure() |
| fig2.add_trace(go.Histogram(x=np.dot(rets, w)*100, nbinsx=50, marker_color='#FF6B00', opacity=0.7, name='Normal')) |
| fig2.add_trace(go.Histogram(x=stress_port*100, nbinsx=50, marker_color='#FF5252', opacity=0.5, name='Stressed')) |
| fig2.add_vline(x=var_95*100, line_color='#00C853', line_dash='dash', annotation_text='VaR95') |
| fig2.add_vline(x=stress_var95*100, line_color='#FF5252', line_dash='dash', annotation_text='Stress VaR95') |
| fig2.update_layout(title='Portfolio Return Distribution: Normal vs Stressed', template='plotly_dark', height=400, paper_bgcolor='#000000', plot_bgcolor='#0a0a0a', font=dict(color='#e6edf3')) |
| data_note = f"\n\n> {synthetic_note}\n" if synthetic_note else "" |
| md = f"## Algorithmic Risk Engine{data_note}\n\n| Metric | Normal | Stressed |\n|--------|--------|----------|\n| Exp Return | {port_ret*100:.1f}% | - |\n| Volatility | {port_vol*100:.1f}% | - |\n| Sharpe | {port_ret/(port_vol+1e-10):.2f} | - |\n| VaR (95%) | {var_95*100:.2f}% | {stress_var95*100:.2f}% |\n| VaR (99%) | {var_99*100:.2f}% | {stress_var99*100:.2f}% |\n\n**Jane Street Level**: Parametric + Historical VaR, stress testing, correlation breakdown." |
| return fig1, fig2, md |
|
|
| def sentiment_analyzer(ticker): |
| df, info = fetch(ticker, "3mo") |
| if df is None or df.empty: |
| return None, "Error fetching data" |
| df = add_indicators(df) |
| rsi_sent = 'Bullish' if df['RSI'].iloc[-1] > 55 else 'Bearish' if df['RSI'].iloc[-1] < 45 else 'Neutral' |
| macd_sent = 'Bullish' if df['MACD'].iloc[-1] > df['MACDS'].iloc[-1] else 'Bearish' |
| vol_sent = 'High Interest' if df['VR'].iloc[-1] > 1.5 else 'Normal' |
| trend_sent = 'Uptrend' if df['Close'].iloc[-1] > df['SMA20'].iloc[-1] > df['SMA50'].iloc[-1] else 'Downtrend' if df['Close'].iloc[-1] < df['SMA20'].iloc[-1] < df['SMA50'].iloc[-1] else 'Mixed' |
| keywords = [] |
| if info: |
| sector = info.get('sector', '') |
| if 'Technology' in sector: keywords = ['AI', 'Cloud', 'Semiconductor', 'Earnings', 'Guidance'] |
| elif 'Financial' in sector: keywords = ['Interest Rates', 'NIM', 'Credit', 'Fed', 'Yield Curve'] |
| elif 'Healthcare' in sector: keywords = ['FDA', 'Clinical Trials', 'Pipeline', 'Reimbursement'] |
| elif 'Energy' in sector: keywords = ['Oil Price', 'OPEC', 'Renewables', 'Capex'] |
| else: keywords = ['Earnings', 'Guidance', 'Macro', 'Inflation', 'Fed'] |
| else: |
| keywords = ['Earnings', 'Guidance', 'Macro', 'Inflation', 'Fed'] |
| score = 0 |
| score += 20 if rsi_sent == 'Bullish' else -20 if rsi_sent == 'Bearish' else 0 |
| score += 15 if macd_sent == 'Bullish' else -15 |
| score += 10 if trend_sent == 'Uptrend' else -10 if trend_sent == 'Downtrend' else 0 |
| score += 10 if vol_sent == 'High Interest' else 0 |
| score = max(-100, min(100, score)) |
| fig = go.Figure() |
| fig.add_trace(go.Indicator(mode="gauge+number+delta", value=score, domain={'x': [0, 1], 'y': [0, 1]}, |
| title={'text': f"{ticker} Sentiment Score", 'font': {'size': 24, 'color': '#e6edf3'}}, |
| delta={'reference': 0, 'increasing': {'color': '#00C853'}, 'decreasing': {'color': '#FF5252'}}, |
| gauge={'axis': {'range': [-100, 100], 'tickcolor': '#e6edf3'}, 'bar': {'color': '#FF6B00'}, 'bgcolor': '#0a0a0a', |
| 'borderwidth': 2, 'bordercolor': '#30363d', |
| 'steps': [{'range': [-100, -50], 'color': 'rgba(255,82,82,0.3)'}, {'range': [-50, 0], 'color': 'rgba(255,107,0,0.2)'}, |
| {'range': [0, 50], 'color': 'rgba(0,212,255,0.2)'}, {'range': [50, 100], 'color': 'rgba(0,200,83,0.3)'}], |
| 'threshold': {'line': {'color': 'white', 'width': 4}, 'thickness': 0.75, 'value': score}})) |
| fig.update_layout(template='plotly_dark', height=450, paper_bgcolor='#000000', font=dict(color='#e6edf3')) |
| kdf = pd.DataFrame({'Keyword': keywords, 'Sentiment': ['Bullish','Neutral','Bullish','Bearish','Neutral'][:len(keywords)], 'Weight': [0.3,0.2,0.25,0.15,0.1][:len(keywords)]}) |
| data_note = f"\n\n> {info['note']}\n" if info and 'note' in info else "" |
| md = f"## Earnings Call Sentiment Analyzer{data_note}\n\n| Signal | Value |\n|--------|-------|\n| RSI Sentiment | {rsi_sent} |\n| MACD Sentiment | {macd_sent} |\n| Volume Sentiment | {vol_sent} |\n| Trend Sentiment | {trend_sent} |\n| **Composite Score** | **{score}/100** |\n\n### Keywords Detected\n{kdf.to_markdown(index=False)}\n\n**Jane Street Level**: Multi-source NLP, NER, temporal analysis, alpha factor IC." |
| return fig, md |
|
|
| def macro_analysis(): |
| macros = {} |
| synthetic_note = "" |
| 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')]: |
| df, info = fetch(t, "3mo") |
| if df is not None and not df.empty: |
| macros[name] = {'price': df['Close'].iloc[-1], '1m': (df['Close'].iloc[-1]/df['Close'].iloc[0]-1)*100, '3m': (df['Close'].iloc[-1]/df['Close'].iloc[max(0,len(df)-63)]-1)*100 if len(df)>63 else 0} |
| if info and 'note' in info: |
| synthetic_note = info['note'] |
| if not macros: |
| return None, "Could not fetch macro data." |
| fig = go.Figure() |
| names = list(macros.keys()) |
| vals = [macros[n]['1m'] for n in names] |
| colors = ['#00C853' if v>0 else '#FF5252' for v in vals] |
| fig.add_trace(go.Bar(x=names, y=vals, marker_color=colors, name='1M Change')) |
| fig.update_layout(title='Cross-Asset Performance (1 Month)', template='plotly_dark', yaxis_title='% Change', height=450, paper_bgcolor='#000000', plot_bgcolor='#0a0a0a', font=dict(color='#e6edf3')) |
| md = "## Global Macro Dashboard\n\n| Asset | Price | 1M Change | 3M Change |\n|-------|-------|-----------|-----------|\n" |
| for n in names: |
| md += f"| {n} | ${macros[n]['price']:.2f} | {macros[n]['1m']:+.1f}% | {macros[n]['3m']:+.1f}% |\n" |
| if synthetic_note: |
| md += f"\n> {synthetic_note}\n" |
| md += "\n**Jane Street Level**: Growth/Inflation quadrant, dollar regime, rate curve, cross-asset momentum." |
| return fig, md |
|
|
| def tech_analysis(ticker, market, period): |
| suffix = MARKETS.get(market, {}).get('suffix', '') |
| if suffix and not any(ticker.endswith(s) for s in suffix.split('|')): |
| ticker = ticker + suffix |
| df, info = fetch(ticker, period) |
| if df is None or df.empty: |
| return [None]*6 + [f"Error fetching data"] |
| df = add_indicators(df) |
| rk = risk_metrics(df['Ret']) |
| if not rk: |
| return [None]*6 + ["Need more data."] |
| l = df.iloc[-1] |
| fig1 = make_subplots(rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.03, row_heights=[0.55, 0.25, 0.20], subplot_titles=(ticker, 'Volume', 'RSI')) |
| fig1.add_trace(go.Candlestick(x=df.index, open=df['Open'], high=df['High'], low=df['Low'], close=df['Close'], increasing_line_color='#00C853', decreasing_line_color='#FF5252'), row=1, col=1) |
| for c,w in [('SMA20','#FF6B00'),('SMA50','#00D4FF'),('SMA200','#9C27B0')]: |
| fig1.add_trace(go.Scatter(x=df.index, y=df[c], line=dict(color=w, width=1), name=c), row=1, col=1) |
| fig1.add_trace(go.Scatter(x=df.index, y=df['BBU'], line=dict(color='gray', width=0.8, dash='dash'), opacity=0.4), row=1, col=1) |
| fig1.add_trace(go.Scatter(x=df.index, y=df['BBL'], line=dict(color='gray', width=0.8, dash='dash'), opacity=0.4), row=1, col=1) |
| colors = ['#00C853' if df['Close'].iloc[i]>=df['Open'].iloc[i] else '#FF5252' for i in range(len(df))] |
| fig1.add_trace(go.Bar(x=df.index, y=df['Volume'], marker_color=colors, opacity=0.7), row=2, col=1) |
| fig1.add_trace(go.Scatter(x=df.index, y=df['RSI'], line=dict(color='#9C27B0', width=1.5), fill='tozeroy'), row=3, col=1) |
| fig1.add_hline(y=70, line_dash="dash", line_color="#FF5252", row=3, col=1) |
| fig1.add_hline(y=30, line_dash="dash", line_color="#00C853", row=3, col=1) |
| fig1.update_layout(title=f'{ticker} Technical Dashboard', template='plotly_dark', height=900, paper_bgcolor='#000000', plot_bgcolor='#0a0a0a', font=dict(color='#e6edf3')) |
| fig2 = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.05, row_heights=[0.6,0.4]) |
| fig2.add_trace(go.Scatter(x=df.index, y=df['MACD'], line=dict(color='#00D4FF', width=1.5), name='MACD'), row=1, col=1) |
| fig2.add_trace(go.Scatter(x=df.index, y=df['MACDS'], line=dict(color='#FF6B00', width=1.5), name='Signal'), row=1, col=1) |
| fig2.add_trace(go.Bar(x=df.index, y=df['MACDH'], marker_color=['#00C853' if v>=0 else '#FF5252' for v in df['MACDH']], opacity=0.6), row=2, col=1) |
| fig2.update_layout(title='MACD', template='plotly_dark', height=450, paper_bgcolor='#000000', plot_bgcolor='#0a0a0a') |
| fig3 = go.Figure() |
| fig3.add_trace(go.Scatter(x=df.index, y=df['pDI'], line=dict(color='#00C853', width=1), name='+DI')) |
| fig3.add_trace(go.Scatter(x=df.index, y=df['mDI'], line=dict(color='#FF5252', width=1), name='-DI')) |
| fig3.add_trace(go.Scatter(x=df.index, y=df['ADX'], line=dict(color='#00D4FF', width=2), name='ADX')) |
| fig3.add_hline(y=25, line_dash="dash", line_color="gray") |
| fig3.update_layout(title='ADX Trend Strength', template='plotly_dark', height=400, paper_bgcolor='#000000', plot_bgcolor='#0a0a0a') |
| fig4 = go.Figure() |
| fig4.add_trace(go.Histogram(x=df['Ret'].dropna()*100, nbinsx=50, marker_color='#FF6B00', opacity=0.7)) |
| fig4.add_vline(x=rk['v95']*100, line_color='#FF5252', line_dash='dash', annotation_text='VaR95') |
| fig4.add_vline(x=df['Ret'].mean()*100, line_color='#00C853', line_dash='dash') |
| fig4.update_layout(title='Return Distribution', template='plotly_dark', height=400, paper_bgcolor='#000000', plot_bgcolor='#0a0a0a') |
| fig5 = go.Figure() |
| fig5.add_trace(go.Scatter(x=df.index, y=df['ATR_pct'], line=dict(color='#FF6B00', width=1.5), fill='tozeroy')) |
| fig5.update_layout(title='ATR % (Volatility)', template='plotly_dark', height=400, paper_bgcolor='#000000', plot_bgcolor='#0a0a0a') |
| fig6 = go.Figure() |
| fig6.add_trace(go.Scatter(x=df.index, y=df['ICH_SA'], line=dict(color='#00C853', width=0.5), name='Senkou A')) |
| fig6.add_trace(go.Scatter(x=df.index, y=df['ICH_SB'], fill='tonexty', fillcolor='rgba(0,200,83,0.1)', line=dict(color='#FF5252', width=0.5), name='Senkou B')) |
| fig6.add_trace(go.Scatter(x=df.index, y=df['Close'], line=dict(color='#00D4FF', width=1.5), name='Price')) |
| fig6.update_layout(title='Ichimoku Cloud', template='plotly_dark', height=400, paper_bgcolor='#000000', plot_bgcolor='#0a0a0a') |
| data_note = f"\n\n> {info['note']}\n" if info and 'note' in info else "" |
| md = f"## {ticker} Technical Analysis{data_note}\n\n| Metric | Value |\n|--------|-------|\n| Price | ${l['Close']:.2f} |\n| RSI | {l['RSI']:.1f} |\n| MACD | {l['MACD']:.3f} |\n| ADX | {l['ADX']:.1f} |\n| ATR % | {l['ATR_pct']:.2f}% |\n| Volume Ratio | {l['VR']:.1f}x |\n\n### Risk Metrics\n| Metric | Value |\n|--------|-------|\n| Ann Return | {rk['ar']*100:.1f}% |\n| Ann Vol | {rk['av']*100:.1f}% |\n| Sharpe | {rk['sh']:.2f} |\n| Max DD | {rk['md']*100:.1f}% |\n| VaR95 | {rk['v95']*100:.2f}% |\n| Win Rate | {rk['wr']*100:.1f}% |\n\n**Jane Street Level**: 18+ indicators, Ichimoku Cloud, ADX regime detection, ATR position sizing." |
| return [fig1, fig2, fig3, fig4, fig5, fig6, md] |
|
|
| def ai_analysis(ticker, market, period): |
| suffix = MARKETS.get(market, {}).get('suffix', '') |
| if suffix and not any(ticker.endswith(s) for s in suffix.split('|')): |
| ticker = ticker + suffix |
| df, info = fetch(ticker, period) |
| if df is None or df.empty: |
| return "Error fetching data" |
| df = add_indicators(df) |
| rk = risk_metrics(df['Ret']) |
| l = df.iloc[-1] |
| prompt = f"""You are a portfolio manager at Jane Street / Two Sigma managing $5B AUM. |
| |
| TICKER: {ticker} |
| PRICE: ${l['Close']:.2f} |
| RSI: {l['RSI']:.1f} |
| MACD: {l['MACD']:.3f} |
| ADX: {l['ADX']:.1f} |
| ATR: {l['ATR_pct']:.2f}% |
| Sharpe: {rk.get('sh',0):.2f} |
| Volatility Regime: {rk.get('vr','unknown')} |
| Max DD: {rk.get('md',0)*100:.1f}% |
| |
| Provide: |
| 1. EXECUTIVE SUMMARY (3 bullets) |
| 2. TECHNICAL INTERPRETATION |
| 3. RISK ASSESSMENT |
| 4. ALPHA SIGNAL (direction + confidence % + time horizon) |
| 5. TRADE RECOMMENDATION (entry, stop, target 1, target 2, position size) |
| 6. CATALYST CALENDAR (next 7 days + next 30 days) |
| 7. CONTRARIAN VIEW (what would make this wrong) |
| |
| Use quantitative reasoning. Reference specific numbers.""" |
| client = K2ThinkClient() |
| return client.chat([{"role":"user","content":prompt}], temperature=0.2, max_tokens=4096) |
|
|
| CSS = """ |
| body { background: #000000 !important; } |
| .gradio-container { background: #000000 !important; color: #e6edf3 !important; } |
| .tabitem { background: #0a0a0a !important; border: 1px solid #1a1a1a !important; border-radius: 8px !important; } |
| .tab-nav { background: #000000 !important; border-bottom: 2px solid #FF6B00 !important; } |
| .tab-nav button { color: #888 !important; background: transparent !important; font-family: 'Roboto Mono', monospace !important; font-size: 0.85em !important; } |
| .tab-nav button.selected { color: #FF6B00 !important; border-bottom: 2px solid #FF6B00 !important; font-weight: bold !important; } |
| input, textarea, select { background: #111 !important; color: #00D4FF !important; border: 1px solid #333 !important; font-family: 'Roboto Mono', monospace !important; } |
| button.primary { background: #FF6B00 !important; color: #000 !important; font-weight: 700 !important; font-family: 'Roboto Mono', monospace !important; border-radius: 4px !important; } |
| button.secondary { background: #1a1a1a !important; color: #FF6B00 !important; border: 1px solid #FF6B00 !important; font-family: 'Roboto Mono', monospace !important; } |
| .markdown-body { color: #e6edf3 !important; font-family: 'Roboto Mono', monospace !important; } |
| .markdown-body h1 { color: #FF6B00 !important; border-bottom: 1px solid #333 !important; font-size: 1.3em !important; } |
| .markdown-body h2 { color: #00D4FF !important; font-size: 1.1em !important; } |
| .markdown-body h3 { color: #00C853 !important; font-size: 1em !important; } |
| .markdown-body table { border-color: #333 !important; font-size: 0.85em !important; } |
| .markdown-body code { background: #1a1a1a !important; color: #00D4FF !important; padding: 2px 6px !important; border-radius: 4px !important; } |
| """ |
|
|
| def build_app(): |
| with gr.Blocks( |
| title="AlphaForge V3.2 - Institutional Quant Platform", |
| theme=gr.themes.Soft(primary_hue="orange", secondary_hue="cyan", neutral_hue="gray", |
| font=[gr.themes.GoogleFont("Roboto Mono"), "monospace"]), |
| css=CSS |
| ) as app: |
| gr.Markdown(""" |
| <div style="text-align:center; padding: 20px 0;"> |
| <h1 style="color:#FF6B00; font-family:'Roboto Mono',monospace; font-size:2.2em; margin:0;"> |
| ALPHAFORGE V3.2 |
| </h1> |
| <p style="color:#888; font-family:'Roboto Mono',monospace; font-size:0.9em; margin:8px 0 0 0;"> |
| Institutional Quant Trading Platform | K2 Think V2 Powered |
| </p> |
| <p style="color:#555; font-family:'Roboto Mono',monospace; font-size:0.75em; margin:4px 0 0 0;"> |
| Multi-Market: US | EU | UK | DE | JP | CN/HK | IN | Crypto | Forex | Commodities | Indices |
| </p> |
| </div> |
| """) |
|
|
| with gr.Tabs(): |
| with gr.TabItem("Technical Analysis"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| ta_ticker = gr.Textbox(label="Ticker", value="AAPL") |
| ta_market = gr.Dropdown(label="Market", choices=list(MARKETS.keys()), value="US Equities") |
| ta_period = gr.Dropdown(label="Period", choices=["1mo","3mo","6mo","1y","2y","5y"], value="1y") |
| ta_btn = gr.Button("Analyze") |
| gr.Markdown("Examples: `AAPL` (US), `AIR.PA` (EU), `7203.T` (JP), `BTC-USD` (Crypto)") |
| with gr.Column(scale=3): |
| ta_out1 = gr.Plot() |
| ta_out2 = gr.Plot() |
| ta_out3 = gr.Plot() |
| with gr.Row(): |
| ta_out4 = gr.Plot() |
| ta_out5 = gr.Plot() |
| ta_out6 = gr.Plot() |
| ta_md = gr.Markdown() |
| ta_btn.click(fn=tech_analysis, inputs=[ta_ticker, ta_market, ta_period], |
| outputs=[ta_out1, ta_out2, ta_out3, ta_out4, ta_out5, ta_out6, ta_md]) |
|
|
| with gr.TabItem("AI Analysis (K2)"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| ai_ticker = gr.Textbox(label="Ticker", value="AAPL") |
| ai_market = gr.Dropdown(label="Market", choices=list(MARKETS.keys()), value="US Equities") |
| ai_period = gr.Dropdown(label="Period", choices=["1mo","3mo","6mo","1y","2y"], value="1y") |
| ai_btn = gr.Button("Generate AI Report") |
| with gr.Column(scale=3): |
| ai_out = gr.Textbox(label="K2 Think V2 Analysis", lines=30) |
| ai_btn.click(fn=ai_analysis, inputs=[ai_ticker, ai_market, ai_period], outputs=ai_out) |
|
|
| with gr.TabItem("Backtest"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| bt_ticker = gr.Textbox(label="Ticker", value="AAPL") |
| bt_strategy = gr.Dropdown(label="Strategy", choices=["Moving Average Crossover","RSI Strategy","MACD Momentum","Mean Reversion","Bollinger Squeeze"], value="Moving Average Crossover") |
| bt_capital = gr.Number(label="Start Capital", value=100000) |
| bt_risk = gr.Slider(label="Risk % per Trade", minimum=1, maximum=10, value=2, step=0.5) |
| bt_period = gr.Dropdown(label="Period", choices=["1y","2y","5y"], value="2y") |
| bt_btn = gr.Button("Run Backtest") |
| with gr.Column(scale=3): |
| bt_eq = gr.Plot() |
| bt_dd = gr.Plot() |
| with gr.Row(): |
| bt_trades = gr.Dataframe() |
| bt_md = gr.Markdown() |
| bt_btn.click(fn=backtest, inputs=[bt_ticker, bt_strategy, bt_capital, bt_risk, bt_period], |
| outputs=[bt_eq, bt_dd, bt_trades, bt_md, gr.Textbox(visible=False)]) |
|
|
| with gr.TabItem("Portfolio Optimizer"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| po_tickers = gr.Textbox(label="Tickers (comma-separated)", value="AAPL, MSFT, GOOGL, AMZN, NVDA") |
| po_period = gr.Dropdown(label="Period", choices=["6mo","1y","2y"], value="1y") |
| po_btn = gr.Button("Optimize Portfolio") |
| with gr.Column(scale=3): |
| po_frontier = gr.Plot() |
| po_pie = gr.Plot() |
| with gr.Row(): |
| po_weights = gr.Dataframe() |
| po_md = gr.Markdown() |
| po_btn.click(fn=optimize_portfolio, inputs=[po_tickers, po_period], |
| outputs=[po_frontier, po_pie, po_weights, po_md]) |
|
|
| with gr.TabItem("Options Pricing"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| op_ticker = gr.Textbox(label="Ticker", value="AAPL") |
| op_type = gr.Dropdown(label="Option Type", choices=["call","put"], value="call") |
| op_strike = gr.Slider(label="Strike % of Spot", minimum=50, maximum=150, value=100, step=1) |
| op_days = gr.Slider(label="Days to Expiry", minimum=7, maximum=365, value=30, step=1) |
| op_rfr = gr.Slider(label="Risk-Free Rate %", minimum=0, maximum=10, value=4.5, step=0.1) |
| op_vol = gr.Slider(label="Vol Override % (0=auto)", minimum=0, maximum=100, value=0, step=1) |
| op_btn = gr.Button("Price Option") |
| with gr.Column(scale=3): |
| op_greeks = gr.Plot() |
| with gr.Row(): |
| op_scenarios = gr.Dataframe() |
| op_md = gr.Markdown() |
| op_btn.click(fn=options_pricing, inputs=[op_ticker, op_strike, op_days, op_rfr, op_vol, op_type], |
| outputs=[op_greeks, op_scenarios, op_md]) |
|
|
| with gr.TabItem("Pairs Trading"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| pt_a = gr.Textbox(label="Asset A", value="AAPL") |
| pt_b = gr.Textbox(label="Asset B", value="MSFT") |
| pt_period = gr.Dropdown(label="Period", choices=["6mo","1y","2y"], value="1y") |
| pt_btn = gr.Button("Analyze Pair") |
| with gr.Column(scale=3): |
| pt_fig = gr.Plot() |
| pt_scat = gr.Plot() |
| pt_md = gr.Markdown() |
| pt_btn.click(fn=pairs_trade, inputs=[pt_a, pt_b, pt_period], outputs=[pt_fig, pt_scat, pt_md]) |
|
|
| with gr.TabItem("Crypto Arbitrage"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| ca_coins = gr.Textbox(label="Coins (comma-separated)", value="BTC, ETH, SOL, XRP") |
| ca_btn = gr.Button("Scan Arbitrage") |
| with gr.Column(scale=3): |
| ca_heatmap = gr.Plot() |
| ca_md = gr.Markdown() |
| ca_btn.click(fn=crypto_arbitrage, inputs=ca_coins, outputs=[ca_heatmap, ca_md]) |
|
|
| with gr.TabItem("Risk Engine"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| re_tickers = gr.Textbox(label="Tickers (comma-separated)", value="AAPL, MSFT, GOOGL, AMZN") |
| re_stress = gr.Textbox(label="Stress Shocks JSON", value='{"AAPL":-10, "AMZN":5}', placeholder='{"AAPL":-10, "TSLA":15}') |
| re_btn = gr.Button("Run Risk Analysis") |
| with gr.Column(scale=3): |
| re_corr = gr.Plot() |
| re_dist = gr.Plot() |
| re_md = gr.Markdown() |
| re_btn.click(fn=risk_engine, inputs=[re_tickers, re_stress], outputs=[re_corr, re_dist, re_md]) |
|
|
| with gr.TabItem("Sentiment"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| se_ticker = gr.Textbox(label="Ticker", value="AAPL") |
| se_btn = gr.Button("Analyze Sentiment") |
| with gr.Column(scale=3): |
| se_gauge = gr.Plot() |
| se_md = gr.Markdown() |
| se_btn.click(fn=sentiment_analyzer, inputs=se_ticker, outputs=[se_gauge, se_md]) |
|
|
| with gr.TabItem("Macro"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| ma_btn = gr.Button("Refresh Macro Dashboard") |
| with gr.Column(scale=3): |
| ma_fig = gr.Plot() |
| ma_md = gr.Markdown() |
| ma_btn.click(fn=macro_analysis, inputs=[], outputs=[ma_fig, ma_md]) |
|
|
| with gr.TabItem("Chat (K2)"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| chat_input = gr.Textbox(label="Ask K2 Think V2", value="Explain gamma scalping", lines=3) |
| chat_btn = gr.Button("Send") |
| with gr.Column(scale=3): |
| chat_output = gr.Textbox(label="Response", lines=30) |
| chat_btn.click( |
| fn=lambda q: K2ThinkClient().chat([{"role":"user","content":q}], temperature=0.4, max_tokens=4096), |
| inputs=chat_input, outputs=chat_output |
| ) |
|
|
| gr.Markdown(""" |
| <div style="text-align:center; padding: 20px 0; margin-top: 20px; border-top: 1px solid #333;"> |
| <p style="color:#555; font-family:'Roboto Mono',monospace; font-size:0.75em;"> |
| AlphaForge V3.2 | For demonstration purposes only. Not financial advice. |
| <br>Configure K2_API_KEY in Space Settings > Repository Secrets for AI features. |
| </p> |
| </div> |
| """) |
|
|
| return app |
|
|
| if __name__ == "__main__": |
| app = build_app() |
| app.launch() |
|
|