Premchan369's picture
V2.1: Fix Gradio compatibility (remove size, show_copy_button params)
6b96f16 verified
"""AlphaForge x K2 Think V2 - Institutional-Grade Quantitative Analysis Platform
Multi-market support: US, EU, Asia, Crypto, Forex, Commodities
Finance-themed UI with dark mode, professional color scheme
Enhanced features: Options pricing, Pairs Trading, Macro Analysis
Powered by MBZUAI K2 Think V2 reasoning model
API Key: set via K2_API_KEY environment variable
"""
import os, json, traceback, warnings, math, random
warnings.filterwarnings('ignore')
# Core imports
try:
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
PLOTLY_OK = True
except ImportError as e:
raise ImportError(f"Missing required package: {e}")
# CONFIG
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"
# K2 THINK V2 CLIENT
class K2ThinkClient:
def __init__(self):
self.api_key = K2_API_KEY
self.available = bool(self.api_key) and len(self.api_key) > 10
self.base_url = K2_BASE_URL
def chat(self, messages, temperature=0.7, max_tokens=4096):
if not self.available:
return "⚠️ K2 Think V2 API Not Configured. Add K2_API_KEY in Space Settings > Repository Secrets. All other features work perfectly!"
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(self.base_url, headers=headers, json=payload, timeout=120)
r.raise_for_status()
j = r.json()
if 'choices' in j and len(j['choices']) > 0:
return j['choices'][0]['message']['content']
return f"⚠️ Unexpected format: {json.dumps(j, indent=2)[:400]}"
except requests.exceptions.Timeout:
return "⏱️ Timeout after 120s. API may be under high load."
except requests.exceptions.HTTPError as e:
if e.response.status_code == 401:
return "🔐 Auth failed. Check K2_API_KEY secret."
elif e.response.status_code == 429:
return "🚦 Rate limited. Wait a moment."
return f"🔴 HTTP {e.response.status_code}: {str(e)[:200]}"
except Exception as e:
return f"🔴 Error: {str(e)[:300]}"
def analyze_market(self, ticker, market, data_summary, tech_summary, timeframe):
prompt = f"""You are an elite quantitative analyst at a top hedge fund (Two Sigma / Jane Street level).
Analyze with deep chain-of-thought reasoning.
## Asset Information
- **Ticker**: {ticker}
- **Market**: {market}
- **Timeframe**: {timeframe}
## Market Data Summary
{data_summary}
## Technical Indicators
{tech_summary}
## Deliverables
Provide exactly these sections:
### 1. Executive Summary (3 bullets)
### 2. Technical Analysis
- Interpret RSI, MACD, Bollinger Bands, ADX, Ichimoku
- Identify support/resistance levels from SMAs and VWAP
### 3. Risk Assessment
- Volatility regime (low/normal/high)
- Tail risk estimate
- Correlation risk
### 4. Alpha Signal
- Direction: BULLISH / NEUTRAL / BEARISH
- Confidence: X%
- Time horizon
- Key conviction drivers
### 5. Trade Recommendation
- Entry price / zone
- Stop-loss
- Target 1 (conservative) and Target 2 (aggressive)
- Position sizing suggestion
### 6. Catalyst Calendar
- Next 7 days
- Next 30 days
### 7. Contrarian View
- What would make this signal wrong?
- Alternative scenario with probability
Think step-by-step. Reference specific numbers."""
return self.chat([{"role": "user", "content": prompt}], temperature=0.2, max_tokens=4096)
def portfolio_advice(self, portfolio_data, corr_data, risk_metrics, market_context):
prompt = f"""You are a CIO managing $2B AUM at a systematic macro fund.
## Portfolio Holdings
{portfolio_data}
## Correlation Analysis
{corr_data}
## Risk Metrics
{risk_metrics}
## Market Context
{market_context}
## Deliverables
### 1. Portfolio Health Score (0-100) with letter grade (A+ to F)
### 2. Concentration Risk
### 3. Correlation Risk Matrix
### 4. Rebalancing Roadmap
- Specific weight adjustments with %
- Timeline: immediate / 1 week / 1 month
### 5. Hedging Strategy
### 6. Expected Return & Risk (Forward 12M)
### 7. Scenario Analysis
- Bull case (20% probability)
- Base case (50% probability)
- Bear case (20% probability)
- Tail case (10% probability)
Use quantitative reasoning throughout."""
return self.chat([{"role": "user", "content": prompt}], temperature=0.2, max_tokens=4096)
def macro_analysis(self, macro_text):
prompt = f"""You are a global macro strategist at Bridgewater / Millennium.
## Input
{macro_text}
## Deliverables
### 1. Macro Regime Classification
### 2. Cross-Asset Implications
- Equities, Fixed Income, FX, Commodities, Crypto
### 3. Trade Ideas (3 concrete setups)
Each with: instrument, direction, entry, stop, target, conviction %, time horizon
### 4. Risk Factors
Think like a macro PM."""
return self.chat([{"role": "user", "content": prompt}], temperature=0.3, max_tokens=4096)
# MARKET DATA
MARKET_PRESETS = {
"🇺🇸 US Equities": {"suffix": "", "examples": "AAPL, TSLA, NVDA, SPY, QQQ, META, AMZN, GOOGL"},
"🇪🇺 European Equities": {"suffix": ".PA", "examples": "AIR.PA, SAN.PA, TTE.PA, OR.PA, MC.PA"},
"🇬🇧 UK Equities": {"suffix": ".L", "examples": "AZN.L, SHEL.L, BP.L, ULVR.L, RIO.L"},
"🇩🇪 German Equities": {"suffix": ".DE", "examples": "SAP.DE, SIE.DE, ALV.DE, BAS.DE, BMW.DE"},
"🇯🇵 Japanese Equities": {"suffix": ".T", "examples": "7203.T, 9984.T, 6861.T, 6758.T"},
"🇨🇳 Chinese Equities": {"suffix": ".HK", "examples": "0700.HK, 9988.HK, 3690.HK, 1810.HK"},
"🇮🇳 Indian Equities": {"suffix": ".NS", "examples": "RELIANCE.NS, TCS.NS, INFY.NS"},
"🪙 Crypto": {"suffix": "", "examples": "BTC-USD, ETH-USD, SOL-USD, XRP-USD"},
"💱 Forex Majors": {"suffix": "=X", "examples": "EURUSD=X, GBPUSD=X, USDJPY=X"},
"🥇 Commodities": {"suffix": "", "examples": "GC=F, SI=F, CL=F, NG=F, ZC=F"},
"📊 Indices": {"suffix": "", "examples": "^GSPC, ^DJI, ^IXIC, ^FTSE, ^N225"},
}
def fetch_data(ticker, period="6mo", interval="1d"):
try:
stock = yf.Ticker(ticker.upper().strip())
df = stock.history(period=period, interval=interval)
if df.empty:
return None, None, f"No data for '{ticker}'. Try examples from the selected market."
info = stock.info
return df, info, None
except Exception as e:
return None, None, f"Error fetching '{ticker}': {str(e)[:200]}"
def calc_indicators(df):
df = df.copy()
df['Ret'] = df['Close'].pct_change()
df['LogRet'] = np.log(df['Close']/df['Close'].shift(1))
df['SMA5'] = df['Close'].rolling(5).mean()
df['SMA20'] = df['Close'].rolling(20).mean()
df['SMA50'] = df['Close'].rolling(50).mean()
df['SMA200'] = df['Close'].rolling(200).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 = d.where(d>0,0).rolling(14).mean()
l = (-d.where(d<0,0)).rolling(14).mean()
df['RSI'] = 100 - (100/(1+g/(l+1e-10)))
m = df['Close'].rolling(20).mean()
s = df['Close'].rolling(20).std()
df['BBU'] = m + 2*s
df['BBL'] = m - 2*s
df['BBP'] = (df['Close']-df['BBL'])/(df['BBU']-df['BBL']+1e-10)
df['BBW'] = (df['BBU']-df['BBL'])/m
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 = df['Low'].rolling(14).min()
hi = 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)
plus_dm = df['High'].diff()
minus_dm = df['Low'].diff()
plus_dm[plus_dm<0] = 0
minus_dm[minus_dm>0] = 0
minus_dm = np.abs(minus_dm)
atr_smooth = tr.ewm(alpha=1/14, adjust=False).mean()
df['plus_DI'] = 100 * (plus_dm.ewm(alpha=1/14, adjust=False).mean() / atr_smooth)
df['minus_DI'] = 100 * (minus_dm.ewm(alpha=1/14, adjust=False).mean() / atr_smooth)
dx = 100 * np.abs(df['plus_DI']-df['minus_DI'])/(df['plus_DI']+df['minus_DI']+1e-10)
df['ADX'] = dx.ewm(alpha=1/14, adjust=False).mean()
df['OBV'] = (np.sign(df['Close'].diff())*df['Volume']).cumsum()
tp_r = (df['High']+df['Low']+df['Close'])/3
tp_diff = tp_r.diff()
pos_flow = tp_r.where(tp_diff>0,0)*df['Volume']
neg_flow = tp_r.where(tp_diff<0,0)*df['Volume']
mfi_pos = pos_flow.rolling(14).sum()
mfi_neg = neg_flow.rolling(14).sum()
df['MFI'] = 100 - (100/(1+mfi_pos/(mfi_neg+1e-10)))
df['ICH_tenkan'] = (df['High'].rolling(9).max()+df['Low'].rolling(9).min())/2
df['ICH_kijun'] = (df['High'].rolling(26).max()+df['Low'].rolling(26).min())/2
df['ICH_senkou_A'] = ((df['ICH_tenkan']+df['ICH_kijun'])/2).shift(26)
df['ICH_senkou_B'] = ((df['High'].rolling(52).max()+df['Low'].rolling(52).min())/2).shift(26)
return df
def calc_risk(df):
r = df['Ret'].dropna()
if len(r) < 30:
return {}
ar = r.mean()*252
av = 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()
v95 = np.percentile(r,5)
v99 = np.percentile(r,1)
cv95 = r[r<=v95].mean() if len(r[r<=v95])>0 else v95
cv99 = r[r<=v99].mean() if len(r[r<=v99])>0 else v99
ca = ar/(abs(md)+1e-10)
roll_sharpe = (r.rolling(63).mean()*252)/(r.rolling(63).std()*np.sqrt(252)+1e-10)
return {'ar':ar,'av':av,'sh':sh,'so':so,'md':md,'v95':v95,'v99':v99,
'cv95':cv95,'cv99':cv99,'ca':ca,'sk':r.skew(),'ku':r.kurtosis(),
'wr':(r>0).mean(),'pf':abs(r[r>0].sum()/(r[r<0].sum()+1e-10)),
'avg_win':r[r>0].mean() if len(r[r>0])>0 else 0,
'avg_loss':r[r<0].mean() if len(r[r<0])>0 else 0,
'roll_sharpe':roll_sharpe.iloc[-1] if len(roll_sharpe.dropna())>0 else 0,
'vol_regime':'low' if av<0.15 else 'normal' if av<0.30 else 'high'}
def calc_signals(df):
l = df.iloc[-1]
p = df.iloc[-2] if len(df)>1 else l
s = {'trend':'neutral','mom':'neutral','vol':'normal','volume':'normal',
'adx_trend':'weak','score':50,'ichimoku':'neutral'}
if l['Close']>l['SMA20']>l['SMA50']>l['SMA200']:
s['trend'] = 'strongly bullish'
elif l['Close']>l['SMA20']>l['SMA50']:
s['trend'] = 'bullish'
elif l['Close']<l['SMA20']<l['SMA50']<l['SMA200']:
s['trend'] = 'strongly bearish'
elif l['Close']<l['SMA20']<l['SMA50']:
s['trend'] = 'bearish'
if l['RSI']<30:
s['mom'] = 'deeply oversold'
elif l['RSI']<40:
s['mom'] = 'oversold'
elif l['RSI']>70:
s['mom'] = 'deeply overbought'
elif l['RSI']>60:
s['mom'] = 'overbought'
elif l['MACD']>l['MACDS'] and p['MACD']<=p['MACDS']:
s['mom'] = 'bullish MACD crossover'
elif l['MACD']<l['MACDS'] and p['MACD']>=p['MACDS']:
s['mom'] = 'bearish MACD crossover'
if l['BBW'] > df['BBW'].quantile(0.9):
s['vol'] = 'expanding (high vol)'
elif l['BBW'] < df['BBW'].quantile(0.1):
s['vol'] = 'contracting (low vol / squeeze)'
if l['VR'] > 2.5:
s['volume'] = 'very heavy (institutional)'
elif l['VR'] > 1.5:
s['volume'] = 'above average'
if l['ADX'] > 25:
s['adx_trend'] = 'strong trend'
elif l['ADX'] > 20:
s['adx_trend'] = 'trending'
if l['Close'] > l['ICH_senkou_A'] and l['Close'] > l['ICH_senkou_B']:
s['ichimoku'] = 'bullish cloud'
elif l['Close'] < l['ICH_senkou_A'] and l['Close'] < l['ICH_senkou_B']:
s['ichimoku'] = 'bearish cloud'
sc = 50
if 'bullish' in s['trend']: sc += 20
if 'bearish' in s['trend']: sc -= 20
if 'oversold' in s['mom']: sc += 10
if 'overbought' in s['mom']: sc -= 10
if 'crossover' in s['mom'] and 'bullish' in s['mom']: sc += 10
if 'crossover' in s['mom'] and 'bearish' in s['mom']: sc -= 10
if l['Close'] > l['VWAP']: sc += 5
if l['Close'] < l['VWAP']: sc -= 5
if l['Stoch_K'] < 20: sc += 5
if l['Stoch_K'] > 80: sc -= 5
if s['ichimoku'] == 'bullish cloud': sc += 5
if s['ichimoku'] == 'bearish cloud': sc -= 5
s['score'] = max(0, min(100, sc))
s['dir'] = 'BULLISH' if sc>60 else 'BEARISH' if sc<40 else 'NEUTRAL'
s['strength'] = 'STRONG' if abs(sc-50)>25 else 'MODERATE' if abs(sc-50)>15 else 'WEAK'
return s
def make_candlestick(df, ticker, market):
fig = make_subplots(rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.03,
row_heights=[0.55, 0.25, 0.20],
subplot_titles=(f'{ticker} ({market})', 'Volume + VWAP', 'RSI'))
fig.add_trace(go.Candlestick(x=df.index, open=df['Open'], high=df['High'],
low=df['Low'], close=df['Close'], name='Price',
increasing_line_color='#00C853', decreasing_line_color='#FF5252'), row=1, col=1)
fig.add_trace(go.Scatter(x=df.index, y=df['SMA20'], line=dict(color='#FF9800', width=1), name='SMA20'), row=1, col=1)
fig.add_trace(go.Scatter(x=df.index, y=df['SMA50'], line=dict(color='#2196F3', width=1), name='SMA50'), row=1, col=1)
fig.add_trace(go.Scatter(x=df.index, y=df['SMA200'], line=dict(color='#9C27B0', width=1.5, dash='dash'), name='SMA200'), row=1, col=1)
fig.add_trace(go.Scatter(x=df.index, y=df['BBU'], line=dict(color='gray', width=0.8, dash='dash'), name='BB+', opacity=0.4), row=1, col=1)
fig.add_trace(go.Scatter(x=df.index, y=df['BBL'], line=dict(color='gray', width=0.8, dash='dash'), name='BB-', 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))]
fig.add_trace(go.Bar(x=df.index, y=df['Volume'], marker_color=colors, name='Volume', opacity=0.7), row=2, col=1)
fig.add_trace(go.Scatter(x=df.index, y=df['VM'], line=dict(color='#FF9800', width=1), name='Vol MA20'), row=2, col=1)
fig.add_trace(go.Scatter(x=df.index, y=df['RSI'], line=dict(color='#9C27B0', width=1.5), fill='tozeroy', fillcolor='rgba(156,39,176,0.1)'), row=3, col=1)
fig.add_hline(y=70, line_dash="dash", line_color="#FF5252", row=3, col=1)
fig.add_hline(y=30, line_dash="dash", line_color="#00C853", row=3, col=1)
fig.add_hline(y=50, line_dash="dot", line_color="gray", row=3, col=1)
fig.update_layout(title=f'{ticker} Technical Analysis', template='plotly_dark',
xaxis_rangeslider_visible=False, height=850,
paper_bgcolor='#0d1117', plot_bgcolor='#161b22',
font=dict(color='#e6edf3'))
fig.update_yaxes(title_text="Price", row=1, col=1)
fig.update_yaxes(title_text="Volume", row=2, col=1)
fig.update_yaxes(title_text="RSI", range=[0,100], row=3, col=1)
return fig
def make_macd(df, ticker):
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.05,
row_heights=[0.6, 0.4], subplot_titles=('MACD', 'Histogram'))
fig.add_trace(go.Scatter(x=df.index, y=df['MACD'], line=dict(color='#2196F3', width=1.5), name='MACD'), row=1, col=1)
fig.add_trace(go.Scatter(x=df.index, y=df['MACDS'], line=dict(color='#FF9800', width=1.5), name='Signal'), row=1, col=1)
colors = ['#00C853' if v>=0 else '#FF5252' for v in df['MACDH']]
fig.add_trace(go.Bar(x=df.index, y=df['MACDH'], marker_color=colors, name='Histogram', opacity=0.7), row=2, col=1)
fig.update_layout(title=f'{ticker} MACD', template='plotly_dark', height=500,
paper_bgcolor='#0d1117', plot_bgcolor='#161b22', font=dict(color='#e6edf3'))
return fig
def make_stoch(df, ticker):
fig = go.Figure()
fig.add_trace(go.Scatter(x=df.index, y=df['Stoch_K'], line=dict(color='#2196F3', width=1.5), name='%K'))
fig.add_trace(go.Scatter(x=df.index, y=df['Stoch_D'], line=dict(color='#FF9800', width=1.5), name='%D'))
fig.add_hline(y=80, line_dash="dash", line_color="#FF5252")
fig.add_hline(y=20, line_dash="dash", line_color="#00C853")
fig.update_layout(title=f'{ticker} Stochastic', template='plotly_dark', height=400,
paper_bgcolor='#0d1117', plot_bgcolor='#161b22', font=dict(color='#e6edf3'))
return fig
def make_vol(df, ticker):
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.05,
row_heights=[0.6, 0.4], subplot_titles=('ATR %', 'Volume Ratio'))
fig.add_trace(go.Scatter(x=df.index, y=df['ATR_pct'], line=dict(color='#FF9800', width=1.5), fill='tozeroy'), row=1, col=1)
fig.add_trace(go.Scatter(x=df.index, y=df['VR'], line=dict(color='#9C27B0', width=1.5)), row=2, col=1)
fig.add_hline(y=1.0, line_dash="dash", line_color="gray", row=2, col=1)
fig.update_layout(title=f'{ticker} Volatility', template='plotly_dark', height=500,
paper_bgcolor='#0d1117', plot_bgcolor='#161b22', font=dict(color='#e6edf3'))
return fig
def make_adx(df, ticker):
fig = go.Figure()
fig.add_trace(go.Scatter(x=df.index, y=df['plus_DI'], line=dict(color='#00C853', width=1), name='+DI'))
fig.add_trace(go.Scatter(x=df.index, y=df['minus_DI'], line=dict(color='#FF5252', width=1), name='-DI'))
fig.add_trace(go.Scatter(x=df.index, y=df['ADX'], line=dict(color='#2196F3', width=2), name='ADX'))
fig.add_hline(y=25, line_dash="dash", line_color="gray")
fig.update_layout(title=f'{ticker} ADX', template='plotly_dark', height=400,
paper_bgcolor='#0d1117', plot_bgcolor='#161b22', font=dict(color='#e6edf3'))
return fig
def make_dist(r, ticker):
fig = go.Figure()
fig.add_trace(go.Histogram(x=r, nbinsx=50, marker_color='#2196F3', opacity=0.7, name='Returns'))
mu, sig = r.mean(), r.std()
fig.add_vline(x=mu, line_color='#00C853', line_dash='dash', annotation_text=f'Mean: {mu*100:.2f}%')
fig.add_vline(x=np.percentile(r,5), line_color='#FF5252', line_dash='dot', annotation_text='VaR95')
fig.update_layout(title=f'{ticker} Returns', xaxis_title='Daily Return', yaxis_title='Count',
height=400, template='plotly_dark',
paper_bgcolor='#0d1117', plot_bgcolor='#161b22', font=dict(color='#e6edf3'))
return fig
# PORTFOLIO
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 = {}
errs = []
for t in ts:
df, info, err = fetch_data(t, period)
if err:
errs.append(err)
elif df is not None and len(df) > 30:
data[t] = df['Close']
if len(data) < 2:
return None, None, None, f"Could not fetch data: {'; '.join(errs[:3])}"
prices = pd.DataFrame(data).dropna()
returns = prices.pct_change().dropna()
if len(returns) < 30:
return None, None, None, "Need more data."
mu = returns.mean() * 252
sigma = returns.cov() * 252
n = len(mu)
best_sh = -999
best_w = np.ones(n)/n
np.random.seed(42)
for _ in range(5000):
w = np.random.dirichlet(np.ones(n)*0.5)
w = np.clip(w, 0, 0.4)
w = w/w.sum()
pr = np.dot(w, mu)
pv = np.sqrt(np.dot(w.T, np.dot(sigma, w)))
sh = pr/(pv+1e-10)
if sh > best_sh:
best_sh = sh
best_w = w
pr = np.dot(best_w, mu)
pv = np.sqrt(np.dot(best_w.T, np.dot(sigma, best_w)))
eq_w = np.ones(n)/n
eq_r = np.dot(eq_w, mu)
eq_v = np.sqrt(np.dot(eq_w.T, np.dot(sigma, eq_w)))
ws = np.random.dirichlet(np.ones(n)*0.5, 3000)
ws = np.clip(ws, 0, 0.4)
ws = ws/ws.sum(axis=1, keepdims=True)
prets = np.dot(ws, mu)
pvols = np.array([np.sqrt(np.dot(w.T, np.dot(sigma, 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='#FF5252', symbol='star'),
text=['Optimal'], textposition='top center', name='Optimal'))
fig.add_trace(go.Scatter(x=[eq_v], y=[eq_r], mode='markers+text',
marker=dict(size=14, color='#FF9800', symbol='diamond'),
text=['Equal'], textposition='bottom center', name='Equal Weight'))
fig.update_layout(title='Efficient Frontier', xaxis_title='Volatility', yaxis_title='Return',
template='plotly_dark', height=550,
paper_bgcolor='#0d1117', plot_bgcolor='#161b22', font=dict(color='#e6edf3'))
corr = returns.corr()
corr_fig = 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')))
corr_fig.update_layout(title='Correlation Matrix', template='plotly_dark', height=450,
paper_bgcolor='#0d1117', plot_bgcolor='#161b22', font=dict(color='#e6edf3'))
wdf = pd.DataFrame({'Ticker': list(data.keys()),
'Optimal (%)': np.round(best_w*100, 2),
'Equal (%)': np.round(eq_w*100, 2)})
md = f"""## Portfolio Results
**Tickers:** {', '.join(list(data.keys()))}
| | Optimal | Equal |
|-|---------|-------|
| Return | {pr*100:.1f}% | {eq_r*100:.1f}% |
| Volatility | {pv*100:.1f}% | {eq_v*100:.1f}% |
| Sharpe | {best_sh:.2f} | {eq_r/(eq_v+1e-10):.2f} |
Improvements: Sharpe {((best_sh/(eq_r/(eq_v+1e-10))-1)*100):+.1f}%
{wdf.to_markdown(index=False)}
"""
return fig, corr_fig, wdf, md
# PAIRS TRADING
def analyze_pair(ticker_a, ticker_b, period="1y"):
df_a, _, _ = fetch_data(ticker_a, period)
df_b, _, _ = fetch_data(ticker_b, period)
if df_a is None or df_b is None:
return None, None, "Could not fetch data for one or both tickers."
prices = pd.DataFrame({ticker_a: df_a['Close'], ticker_b: df_b['Close']}).dropna()
if len(prices) < 30:
return None, None, "Insufficient aligned data."
spread = prices[ticker_a] - prices[ticker_b]
spread_norm = (spread - spread.mean()) / spread.std()
beta = np.polyfit(prices[ticker_b], prices[ticker_a], 1)[0]
hedge_ratio = beta
spread_hedged = prices[ticker_a] - hedge_ratio * prices[ticker_b]
spread_hedged_norm = (spread_hedged - spread_hedged.mean()) / spread_hedged.std()
lag_spread = spread_hedged.shift(1)
delta_spread = spread_hedged.diff()
valid = delta_spread.dropna().index
y = delta_spread.loc[valid]
x = lag_spread.loc[valid] - spread_hedged.mean()
theta = -np.polyfit(x, y, 1)[0]
half_life = np.log(2)/theta if theta > 0 else float('inf')
z = spread_hedged_norm.iloc[-1]
signal = 'SHORT SPREAD' if z > 2 else 'LONG SPREAD' if z < -2 else 'NO SIGNAL'
fig = make_subplots(rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.05,
subplot_titles=(f'{ticker_a} vs {ticker_b}', 'Normalized Spread', 'Z-Score'))
fig.add_trace(go.Scatter(x=prices.index, y=prices[ticker_a], line=dict(color='#2196F3', width=1.5), name=ticker_a), row=1, col=1)
fig.add_trace(go.Scatter(x=prices.index, y=prices[ticker_b], line=dict(color='#FF9800', width=1.5), name=ticker_b), row=1, col=1)
fig.add_trace(go.Scatter(x=prices.index, y=spread_norm, line=dict(color='#00C853', width=1.5), fill='tozeroy'), row=2, col=1)
fig.add_trace(go.Scatter(x=prices.index, y=spread_hedged_norm, line=dict(color='#9C27B0', width=1.5)), row=3, col=1)
fig.add_hline(y=2, line_dash="dash", line_color="#FF5252", row=3, col=1)
fig.add_hline(y=-2, line_dash="dash", line_color="#00C853", row=3, col=1)
fig.add_hline(y=0, line_dash="dot", line_color="gray", row=3, col=1)
fig.update_layout(title=f'Pairs: {ticker_a} / {ticker_b}', template='plotly_dark',
height=750, paper_bgcolor='#0d1117', plot_bgcolor='#161b22', font=dict(color='#e6edf3'))
scat = go.Figure()
scat.add_trace(go.Scatter(x=prices[ticker_b], y=prices[ticker_a], mode='markers',
marker=dict(size=4, color=np.arange(len(prices)), colorscale='Viridis', showscale=True),
name='Price Path'))
x_range = np.linspace(prices[ticker_b].min(), prices[ticker_b].max(), 100)
intercept = np.polyfit(prices[ticker_b], prices[ticker_a], 1)[1]
y_range = hedge_ratio * x_range + intercept
scat.add_trace(go.Scatter(x=x_range, y=y_range, mode='lines',
line=dict(color='#FF5252', dash='dash'), name=f'OLS (β={hedge_ratio:.2f})'))
scat.update_layout(title=f'Price Relationship (β={hedge_ratio:.2f})', template='plotly_dark',
xaxis_title=ticker_b, yaxis_title=ticker_a, height=450,
paper_bgcolor='#0d1117', plot_bgcolor='#161b22', font=dict(color='#e6edf3'))
md = f"""## Pairs Trading: {ticker_a} vs {ticker_b}
| Metric | Value |
|--------|-------|
| Hedge Ratio (β) | {hedge_ratio:.3f} |
| Spread Mean | ${spread_hedged.mean():.2f} |
| Spread Std | ${spread_hedged.std():.2f} |
| Current Z-Score | {z:.2f} |
| Half-Life | {half_life:.1f} days |
### Signal
| Z-Score | Action |
|---------|--------|
| {z:.2f} | **{signal}** |
### Rules
- **Long Spread** when Z < -2 (buy {ticker_a}, short {ticker_b})
- **Short Spread** when Z > +2 (short {ticker_a}, buy {ticker_b})
- **Exit** when Z crosses 0
- **Stop Loss** when |Z| > 3.5
"""
return fig, scat, md
# OPTIONS
def black_scholes(S, K, T, r, sigma, option_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 = norm.cdf(d1)
nd2 = norm.cdf(d2)
npdf_d1 = norm.pdf(d1)
except:
def approx_cdf(x):
return 0.5 * (1 + math.erf(x / math.sqrt(2)))
nd1 = approx_cdf(d1)
nd2 = approx_cdf(d2)
npdf_d1 = (1/math.sqrt(2*math.pi)) * math.exp(-0.5*d1**2)
if option_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_d1 / (S*sigma*np.sqrt(T))
theta = -(S*npdf_d1*sigma)/(2*np.sqrt(T)) - r*K*math.exp(-r*T)*nd2 if option_type=='call' else -(S*npdf_d1*sigma)/(2*np.sqrt(T)) + r*K*math.exp(-r*T)*(1-nd2)
vega = S*npdf_d1*np.sqrt(T)
rho = K*T*math.exp(-r*T)*nd2 if option_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 analyze_options(ticker, strike_pct, days, rfr, vol_override, option_type):
df, info, err = fetch_data(ticker, "6mo")
if df is None:
return None, None, f"Error: {err}"
df = calc_indicators(df)
S = df['Close'].iloc[-1]
K = S * (strike_pct/100)
T = days / 365
if vol_override and vol_override > 0:
sigma = vol_override / 100
else:
sigma = df['Ret'].dropna().std() * np.sqrt(252)
r = rfr / 100
bs = black_scholes(S, K, T, r, sigma, option_type.lower())
if 'error' in bs:
return None, None, f"BS Error: {bs['error']}"
pct_changes = np.arange(-30, 31, 5)
pl_data = []
for pct in pct_changes:
new_S = S * (1 + pct/100)
new_bs = black_scholes(new_S, K, max(T - 1/365, 0.001), r, sigma, option_type.lower())
pl = (new_bs['price'] - bs['price']) * 100
pl_data.append({'Price Change %': f'{pct:+d}%', 'Stock Price': f'${new_S:.2f}',
'Option Price': f'${new_bs["price"]:.2f}', 'P/L (per 100)': f'${pl:+.2f}'})
pl_df = pd.DataFrame(pl_data)
strikes = np.linspace(S*0.7, S*1.3, 50)
greeks_data = {'price': [], 'delta': [], 'gamma': [], 'theta': [], 'vega': []}
for st in strikes:
res = black_scholes(S, st, T, r, sigma, option_type.lower())
for k in greeks_data:
greeks_data[k].append(res.get(k, 0))
fig = make_subplots(rows=2, cols=3,
subplot_titles=('Price', 'Delta', 'Gamma', 'Theta (daily)', 'Vega', 'P/L at Expiry'),
vertical_spacing=0.12, horizontal_spacing=0.08)
colors = ['#2196F3', '#00C853', '#FF9800', '#FF5252', '#9C27B0', '#673AB7']
for i, (k, v) in enumerate(greeks_data.items()):
row, col = (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=row, col=col)
fig.add_vline(x=S, line_dash='dash', line_color='gray', row=row, col=col)
expiry_payoff = [max(s-K,0) if option_type.lower()=='call' else max(K-s,0) for s in strikes]
pl_expiry = [p - bs['price'] for p in expiry_payoff]
fig.add_trace(go.Scatter(x=strikes, y=pl_expiry, line=dict(color='#673AB7', width=2), name='P/L Expiry'), 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} {option_type.title()} Greeks (S=${S:.2f}, K=${K:.2f}, T={days}d, σ={sigma*100:.1f}%)',
template='plotly_dark', height=650,
paper_bgcolor='#0d1117', plot_bgcolor='#161b22', font=dict(color='#e6edf3'))
md = f"""## {ticker} {option_type.title()} Analysis
| Parameter | Value |
|-----------|-------|
| Spot (S) | ${S:.2f} |
| Strike (K) | ${K:.2f} ({strike_pct:.0f}% of spot) |
| Time to Expiry | {days} days |
| Risk-Free Rate | {r*100:.2f}% |
| Volatility | {sigma*100:.1f}% |
### Greeks
| Greek | Value |
|-------|-------|
| **Price** | ${bs['price']:.3f} |
| **Delta** | {bs['delta']:.4f} |
| **Gamma** | {bs['gamma']:.6f} |
| **Theta** | ${bs['theta']:.4f}/day |
| **Vega** | ${bs['vega']:.4f} |
| **Rho** | ${bs['rho']:.4f} |
| **d1** | {bs['d1']:.4f} |
| **d2** | {bs['d2']:.4f} |
### P/L Scenarios (per 100 contracts)
{pl_df.to_markdown(index=False)}
"""
return fig, pl_df, md
# MACRO
def get_macro_data():
macros = {}
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')]:
try:
df = yf.Ticker(t).history(period='1mo')
if not df.empty:
macros[name] = {'price': df['Close'].iloc[-1], 'change_1m': (df['Close'].iloc[-1]/df['Close'].iloc[0]-1)*100}
except:
pass
return macros
def ai_macro():
macros = get_macro_data()
macro_text = "Global Macro Snapshot:\n"
for name, data in macros.items():
macro_text += f"- {name}: ${data['price']:.2f} (1M change: {data['change_1m']:+.1f}%)\n"
client = K2ThinkClient()
return client.macro_analysis(macro_text)
# UI FUNCTIONS
def analyze_stock(ticker, market_preset, period, interval):
ticker = ticker.strip().upper()
if not ticker:
return [None]*6 + ["Enter a ticker."]
suffix = MARKET_PRESETS.get(market_preset, {}).get('suffix', '')
if suffix and not any(ticker.endswith(s) for s in suffix.split('|')):
ticker = ticker + suffix
df, info, err = fetch_data(ticker, period)
if df is None:
return [None]*6 + [f"Error: {err}"]
df = calc_indicators(df)
sg = calc_signals(df)
rk = calc_risk(df)
if not rk:
return [None]*6 + ["Need more data."]
l = df.iloc[-1]
p = df.iloc[-2] if len(df)>1 else l
ch = ((l['Close']/p['Close']-1)*100) if p['Close']>0 else 0
c1 = make_candlestick(df, ticker, market_preset)
c2 = make_macd(df, ticker)
c3 = make_stoch(df, ticker)
c4 = make_vol(df, ticker)
c5 = make_adx(df, ticker)
c6 = make_dist(df['Ret'].dropna(), ticker)
info_lines = []
if info:
info_lines.append(f"| Name | {info.get('longName', ticker)} |")
info_lines.append(f"| Sector | {info.get('sector', 'N/A')} |")
info_lines.append(f"| Industry | {info.get('industry', 'N/A')} |")
if info.get('marketCap'):
info_lines.append(f"| Market Cap | {info.get('marketCap'):,} |")
if info.get('fiftyTwoWeekHigh'):
info_lines.append(f"| 52W High | ${info.get('fiftyTwoWeekHigh'):.2f} |")
if info.get('fiftyTwoWeekLow'):
info_lines.append(f"| 52W Low | ${info.get('fiftyTwoWeekLow'):.2f} |")
if info.get('trailingPE'):
info_lines.append(f"| P/E | {info.get('trailingPE'):.2f} |")
mkt_info = "\n".join(info_lines)
md = f"""# {ticker} - {sg['dir']} {sg['strength']} (Score: {sg['score']}/100)
**Price:** ${l['Close']:.2f} | **Change:** {ch:+.2f}% | **Period:** {period}
{mkt_info}
## Signal Dashboard
| Indicator | Value | Signal |
|-----------|-------|--------|
| RSI (14) | {l['RSI']:.1f} | {'🟢 Deeply Oversold' if l['RSI']<30 else '🔴 Deeply Overbought' if l['RSI']>70 else '⚪ Neutral'} |
| MACD | {l['MACD']:.3f} | {'🟢 Bullish' if l['MACD']>l['MACDS'] else '🔴 Bearish'} |
| Bollinger | {l['BBP']:.1%} | {'🔴 Upper' if l['BBP']>0.8 else '🟢 Lower' if l['BBP']<0.2 else '⚪ Mid'} |
| VWAP | {'🟢 Above' if l['Close']>l['VWAP'] else '🔴 Below'} | {'🟢 Bullish' if l['Close']>l['VWAP'] else '🔴 Bearish'} |
| Stoch %K | {l['Stoch_K']:.1f} | {'🟢 Oversold' if l['Stoch_K']<20 else '🔴 Overbought' if l['Stoch_K']>80 else '⚪ Neutral'} |
| ADX | {l['ADX']:.1f} | {sg['adx_trend']} |
| Volume | {l['VR']:.1f}x avg | {'🔥 Heavy' if l['VR']>2 else '⬆️ Above Avg' if l['VR']>1.5 else '⚪ Normal'} |
| Trend | {sg['trend'].upper()} | — |
| Momentum | {sg['mom']} | — |
| Volatility | {sg['vol']} | — |
| Ichimoku | {sg['ichimoku']} | — |
## Risk Metrics
| Metric | Value |
|--------|-------|
| Ann. Return | {rk['ar']*100:.1f}% |
| Ann. Volatility | {rk['av']*100:.1f}% |
| Vol Regime | {rk['vol_regime'].upper()} |
| Sharpe | {rk['sh']:.2f} |
| Sortino | {rk['so']:.2f} |
| Max Drawdown | {rk['md']*100:.1f}% |
| VaR (95%) | {rk['v95']*100:.2f}% |
| VaR (99%) | {rk['v99']*100:.2f}% |
| CVaR (95%) | {rk['cv95']*100:.2f}% |
| CVaR (99%) | {rk['cv99']*100:.2f}% |
| Calmar | {rk['ca']:.2f} |
| Win Rate | {rk['wr']*100:.1f}% |
| Avg Win | {rk['avg_win']*100:.2f}% |
| Avg Loss | {rk['avg_loss']*100:.2f}% |
| Profit Factor | {rk['pf']:.2f} |
| Skewness | {rk['sk']:.2f} |
| Kurtosis | {rk['ku']:.2f} |
| 63D Rolling Sharpe | {rk['roll_sharpe']:.2f} |
"""
return [c1, c2, c3, c4, c5, c6, md]
def ai_analyze_stock(ticker, market_preset, period, interval):
ticker = ticker.strip().upper()
if not ticker:
return "Enter a ticker."
suffix = MARKET_PRESETS.get(market_preset, {}).get('suffix', '')
if suffix and not any(ticker.endswith(s) for s in suffix.split('|')):
ticker = ticker + suffix
df, info, err = fetch_data(ticker, period)
if df is None:
return f"Error: {err}"
df = calc_indicators(df)
sg = calc_signals(df)
rk = calc_risk(df)
l = df.iloc[-1]
p = df.iloc[-2] if len(df)>1 else l
ch = ((l['Close']/p['Close']-1)*100) if p['Close']>0 else 0
data_sum = f"""Ticker: {ticker}
Price: ${l['Close']:.2f} (Change: {ch:+.2f}%)
Period: {period} | Data Points: {len(df)}
SMA20: ${l['SMA20']:.2f} | SMA50: ${l['SMA50']:.2f} | SMA200: ${l['SMA200']:.2f}
52W Range: ${df['Low'].min():.2f} - ${df['High'].max():.2f}
ATR: ${l['ATR']:.2f} ({l['ATR_pct']:.1f}% of price)
Volume: {l['Volume']:,.0f} ({l['VR']:.1f}x avg)
"""
tech_sum = f"""RSI: {l['RSI']:.1f} | MACD: {l['MACD']:.3f} vs Signal: {l['MACDS']:.3f} | MACD Hist: {l['MACDH']:.3f}
BB Position: {l['BBP']:.1%} | BB Width: {l['BBW']:.2f} | Stoch %K: {l['Stoch_K']:.1f}
VWAP: ${l['VWAP']:.2f} | ADX: {l['ADX']:.1f} | MFI: {l['MFI']:.1f}
Ichimoku: {sg['ichimoku']} | Cloud: Senkou A={l['ICH_senkou_A']:.2f}, B={l['ICH_senkou_B']:.2f}
Score: {sg['score']}/100 | Direction: {sg['dir']} | Strength: {sg['strength']}
Risk: Sharpe={rk.get('sh',0):.2f}, Vol={rk.get('av',0)*100:.1f}%, MaxDD={rk.get('md',0)*100:.1f}%, VaR95={rk.get('v95',0)*100:.2f}%"""
client = K2ThinkClient()
return client.analyze_market(ticker, market_preset, data_sum, tech_sum, period)
def ai_portfolio(tickers, period):
fig, corr_fig, wdf, md = optimize_portfolio(tickers, period)
if fig is None:
return f"Error: {md}"
pd_str = f"Tickers: {', '.join(wdf['Ticker'].tolist())}\nWeights: {', '.join([f'{t}: {w:.1f}%' for t,w in zip(wdf['Ticker'], wdf['Optimal (%)'])])}"
corr_str = f"Correlations:\n{wdf['Ticker'].tolist()}"
client = K2ThinkClient()
return client.portfolio_advice(pd_str, corr_str, md, "Current macro: mixed signals, rates elevated, geopolitical uncertainty")
def ai_chat(question, temp):
if not question.strip():
return "Enter a question."
client = K2ThinkClient()
return client.chat([{"role":"user","content":question}], temperature=temp, max_tokens=4096)
# GRADIO APP
def build_app():
with gr.Blocks(
title="AlphaForge x K2 Think V2 - Institutional Quant Platform",
theme=gr.themes.Soft(
primary_hue="blue", secondary_hue="indigo", neutral_hue="slate",
font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"]
),
css="""
body { background: #0d1117 !important; }
.gradio-container { background: #0d1117 !important; color: #e6edf3 !important; }
.tabitem { background: #161b22 !important; border: 1px solid #30363d !important; border-radius: 12px !important; }
.tab-nav { background: #0d1117 !important; border-bottom: 1px solid #30363d !important; }
.tab-nav button { color: #8b949e !important; background: transparent !important; border: none !important; }
.tab-nav button.selected { color: #58a6ff !important; border-bottom: 2px solid #58a6ff !important; }
input, textarea, select { background: #21262d !important; color: #e6edf3 !important; border: 1px solid #30363d !important; }
button.primary { background: linear-gradient(135deg, #1f6feb, #58a6ff) !important; color: white !important; border: none !important; border-radius: 8px !important; font-weight: 600 !important; }
button.secondary { background: #21262d !important; color: #58a6ff !important; border: 1px solid #30363d !important; border-radius: 8px !important; }
.title-bar { text-align: center; padding: 24px 0; }
.title-bar h1 { font-size: 2.8em; font-weight: 800; margin: 0; background: linear-gradient(90deg, #58a6ff, #a371f7); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.title-bar p { color: #8b949e; font-size: 1.1em; margin-top: 8px; }
.badge-row { text-align: center; margin: 16px 0 24px; }
.badge { display: inline-block; padding: 6px 14px; margin: 4px; border-radius: 20px; font-size: 0.85em; font-weight: 600; }
.badge-api { background: linear-gradient(135deg, #1f6feb, #a371f7); color: white; }
.badge-data { background: #238636; color: white; }
.badge-alpha { background: #8957e5; color: white; }
.k2-active { text-align: center; padding: 8px; margin: 8px 0; border-radius: 8px; font-size: 0.9em; background: rgba(35,134,54,0.2); color: #3fb950; border: 1px solid #238636; }
.k2-inactive { text-align: center; padding: 8px; margin: 8px 0; border-radius: 8px; font-size: 0.9em; background: rgba(209,36,47,0.2); color: #f85149; border: 1px solid #da3633; }
"""
) as demo:
gr.HTML("""
<div class="title-bar">
<h1>🔥 AlphaForge x K2 Think V2</h1>
<p>Institutional-Grade Quantitative Analysis Platform - Powered by MBZUAI's State-of-the-Art Reasoning Model</p>
</div>
<div class="badge-row">
<span class="badge badge-api">🤖 K2 Think V2</span>
<span class="badge badge-data">📊 Multi-Market</span>
<span class="badge badge-alpha">🎯 AI Alpha</span>
<span class="badge badge-data">📐 Options</span>
<span class="badge badge-alpha">🔗 Pairs</span>
<span class="badge badge-api">🌍 Macro</span>
</div>
""")
k2_cls = "k2-active" if K2_API_KEY else "k2-inactive"
k2_txt = "✅ K2 Think V2 API Connected" if K2_API_KEY else "⚠️ K2 Think V2 Not Configured — Add K2_API_KEY in Space Settings > Repository Secrets"
gr.HTML(f'<div class="{k2_cls}">{k2_txt}</div>')
with gr.Tab("📈 Technical Analysis"):
with gr.Row():
with gr.Column(scale=1):
mkt_select = gr.Dropdown(label="🌍 Market", choices=list(MARKET_PRESETS.keys()), value="🇺🇸 US Equities")
ticker_in = gr.Textbox(label="Ticker", value="AAPL", placeholder="e.g., AAPL, BTC-USD, EURUSD=X")
period_in = gr.Dropdown(label="Period", choices=["1mo","3mo","6mo","1y","2y","5y"], value="6mo")
interval_in = gr.Dropdown(label="Interval", choices=["1d","1wk","1mo"], value="1d")
analyze_btn = gr.Button("🔍 Analyze", variant="primary")
ai_btn = gr.Button("🤖 AI Deep Analysis (K2 Think V2)", variant="secondary")
with gr.Column(scale=2):
summary_out = gr.Markdown()
with gr.Row():
chart1 = gr.Plot(label="Price & Technicals")
chart2 = gr.Plot(label="MACD")
with gr.Row():
chart3 = gr.Plot(label="Stochastic")
chart4 = gr.Plot(label="Volatility & Volume")
with gr.Row():
chart5 = gr.Plot(label="ADX Trend Strength")
chart6 = gr.Plot(label="Return Distribution")
with gr.Row():
ai_out = gr.Textbox(label="🤖 K2 Think V2 Analysis", lines=30, max_lines=50)
analyze_btn.click(fn=analyze_stock, inputs=[ticker_in, mkt_select, period_in, interval_in],
outputs=[chart1, chart2, chart3, chart4, chart5, chart6, summary_out])
ai_btn.click(fn=ai_analyze_stock, inputs=[ticker_in, mkt_select, period_in, interval_in],
outputs=[ai_out])
with gr.Tab("💼 Portfolio Optimizer"):
with gr.Row():
with gr.Column(scale=1):
port_in = gr.Textbox(label="Tickers (comma-separated)", value="AAPL, MSFT, GOOGL, AMZN, NVDA")
port_period = gr.Dropdown(label="Lookback", choices=["6mo","1y","2y","3y"], value="1y")
opt_btn = gr.Button("🎯 Optimize", variant="primary")
ai_port_btn = gr.Button("🤖 AI Portfolio Advice", variant="secondary")
with gr.Column(scale=2):
port_md = gr.Markdown()
with gr.Row():
frontier_plot = gr.Plot(label="Efficient Frontier")
corr_plot = gr.Plot(label="Correlation Matrix")
with gr.Row():
weights_df = gr.DataFrame(label="Optimal Weights", interactive=False)
with gr.Row():
ai_port_out = gr.Textbox(label="🤖 AI Portfolio Advice", lines=25, max_lines=40)
opt_btn.click(fn=optimize_portfolio, inputs=[port_in, port_period], outputs=[frontier_plot, corr_plot, weights_df, port_md])
ai_port_btn.click(fn=ai_portfolio, inputs=[port_in, port_period], outputs=[ai_port_out])
with gr.Tab("🔗 Pairs Trading"):
with gr.Row():
with gr.Column(scale=1):
pair_a = gr.Textbox(label="Ticker A (Long)", value="AAPL")
pair_b = gr.Textbox(label="Ticker B (Short)", value="MSFT")
pair_period = gr.Dropdown(label="Lookback", choices=["6mo","1y","2y"], value="1y")
pair_btn = gr.Button("Analyze Pair", variant="primary")
with gr.Column(scale=2):
pair_md = gr.Markdown()
with gr.Row():
pair_chart = gr.Plot(label="Spread Analysis")
pair_scatter = gr.Plot(label="Price Relationship")
pair_btn.click(fn=analyze_pair, inputs=[pair_a, pair_b, pair_period], outputs=[pair_chart, pair_scatter, pair_md])
with gr.Tab("📐 Options Pricing"):
with gr.Row():
with gr.Column(scale=1):
opt_ticker = gr.Textbox(label="Underlying Ticker", value="AAPL")
opt_type = gr.Dropdown(label="Option Type", choices=["Call","Put"], value="Call")
opt_strike = gr.Slider(label="Strike (% of spot)", minimum=70, maximum=130, value=100, step=1)
opt_days = gr.Slider(label="Days to Expiry", minimum=7, maximum=365, value=30, step=7)
opt_rfr = gr.Slider(label="Risk-Free Rate (%)", minimum=0, maximum=10, value=4.5, step=0.25)
opt_vol = gr.Number(label="Vol Override (%)", value=0, info="0 = use historical vol")
opt_calc_btn = gr.Button("📐 Calculate Greeks", variant="primary")
with gr.Column(scale=2):
opt_md = gr.Markdown()
with gr.Row():
greeks_plot = gr.Plot(label="Greeks Analysis")
opt_pl = gr.DataFrame(label="P/L Scenarios", interactive=False)
opt_calc_btn.click(fn=analyze_options, inputs=[opt_ticker, opt_strike, opt_days, opt_rfr, opt_vol, opt_type],
outputs=[greeks_plot, opt_pl, opt_md])
with gr.Tab("🌍 Macro Analysis"):
with gr.Row():
macro_btn = gr.Button("🌍 Analyze Global Macro (K2 Think V2)", variant="primary")
with gr.Row():
macro_out = gr.Textbox(label="🤖 K2 Think V2 Macro Analysis", lines=40, max_lines=60)
macro_btn.click(fn=ai_macro, outputs=[macro_out])
with gr.Tab("💬 K2 Think V2 Chat"):
gr.Markdown("## Direct Chat with K2 Think V2")
gr.Markdown("Ask any financial question - strategy, market analysis, quant interview prep, portfolio advice.")
with gr.Row():
chat_in = gr.Textbox(label="Your Question", placeholder="e.g., 'Explain gamma scalping with a real trade example'", lines=4)
chat_temp = gr.Slider(label="Temp", minimum=0, maximum=1, value=0.4, step=0.1)
chat_btn = gr.Button("🚀 Ask K2 Think V2", variant="primary")
chat_out = gr.Textbox(label="🤖 Response", lines=30, max_lines=50)
chat_btn.click(fn=ai_chat, inputs=[chat_in, chat_temp], outputs=[chat_out])
with gr.Tab("ℹ️ About & Setup"):
gr.Markdown(f"""
## AlphaForge x K2 Think V2
Built for the **Build with K2 Think V2 Challenge** by MBZUAI.
### Features
| Feature | Description |
|---------|-------------|
| **📈 Technical Analysis** | 18+ indicators: RSI, MACD, Bollinger, VWAP, Stochastic, ADX, Ichimoku, ATR, MFI, OBV |
| **🌍 Multi-Market** | US, EU, UK, DE, JP, CN, IN equities + Crypto + Forex + Commodities + Indices |
| **💼 Portfolio** | Mean-variance optimization, efficient frontier, correlation matrix |
| **🔗 Pairs Trading** | Cointegration analysis, hedge ratio, half-life, Z-score signals |
| **📐 Options Pricing** | Black-Scholes + full Greeks (Delta, Gamma, Theta, Vega, Rho), P/L scenarios |
| **🌍 Macro Analysis** | Global cross-asset regime analysis via K2 Think V2 |
| **🤖 AI Analysis** | K2 Think V2 chain-of-thought: entry/stop/target, catalyst calendar, contrarian view |
| **💬 AI Chat** | Ask any financial question with adjustable temperature |
### Setup
> **K2 Think V2 API Key**
> 1. Space Settings → Repository secrets
> 2. New secret: `K2_API_KEY` = `IFM-4SpQ0qEg0Wlsw04O`
> 3. Save → Factory Rebuild
**Without API key:** All technical analysis, charts, portfolio optimization, pairs trading, and options pricing work perfectly!
### Links
- [Full AlphaForge](https://huggingface.co/Premchan369/alphaforge-quant-system) (25 quant modules)
- [Q-TensorFormer](https://huggingface.co/Premchan369/Q-TensorFormer) (Quantum AI)
- [Challenge](https://build.k2think.ai/)
- [MBZUAI](https://mbzuai.ac.ae/)
*Built by Premchan | Build with K2 Think V2*
""")
return demo
if __name__ == "__main__":
demo = build_app()
demo.queue().launch(server_name="0.0.0.0", server_port=7860)