Premchan369 commited on
Commit
8b6fafc
Β·
verified Β·
1 Parent(s): f65410e

Fix share=False to share=True for HF Spaces

Browse files
Files changed (1) hide show
  1. app.py +993 -0
app.py ADDED
@@ -0,0 +1,993 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """AlphaForge x K2 Think V2 β€” Elite Quant Trading Demo
2
+
3
+ Built for the Build with K2 Think V2 Challenge (MBZUAI)
4
+
5
+ Features:
6
+ - K2 Think V2 API integration for AI-powered financial reasoning
7
+ - Real-time stock data via yfinance
8
+ - Technical analysis (RSI, MACD, Bollinger, VWAP)
9
+ - Portfolio optimization & risk metrics
10
+ - Alpha signal generation
11
+ - AI-powered market analysis with chain-of-thought reasoning
12
+
13
+ API Key: set via K2_API_KEY environment variable
14
+ """
15
+ import os
16
+ import json
17
+ import time
18
+ import gradio as gr
19
+ import requests
20
+ import yfinance as yf
21
+ import pandas as pd
22
+ import numpy as np
23
+ from datetime import datetime, timedelta
24
+ import plotly.graph_objects as go
25
+ from plotly.subplots import make_subplots
26
+ from scipy import stats
27
+ from scipy.optimize import minimize
28
+ import warnings
29
+ warnings.filterwarnings('ignore')
30
+
31
+
32
+ # ──────────────────────────────────────────────────────────────
33
+ # K2 Think V2 API Configuration
34
+ # ──────────────────────────────────────────────────────────────
35
+ K2_API_KEY = os.environ.get("K2_API_KEY")
36
+ K2_BASE_URL = "https://api.k2think.ai/v1/chat/completions"
37
+ K2_MODEL = "MBZUAI-IFM/K2-Think-v2"
38
+
39
+
40
+ # ──────────────────────────────────────────────────────────────
41
+ # K2 Think V2 API Client
42
+ # ──────────────────────────────────────────────────────────────
43
+ class K2ThinkClient:
44
+ """Client for K2 Think V2 API β€” state-of-the-art reasoning model"""
45
+
46
+ def __init__(self, api_key: str = K2_API_KEY, base_url: str = K2_BASE_URL):
47
+ self.api_key = api_key
48
+ self.base_url = base_url
49
+ self.headers = {
50
+ "accept": "application/json",
51
+ "Authorization": f"Bearer {api_key}",
52
+ "Content-Type": "application/json"
53
+ }
54
+
55
+ def chat(self, messages: list, temperature: float = 0.7,
56
+ max_tokens: int = 4096, stream: bool = False) -> str:
57
+ """Send chat completion request to K2 Think V2"""
58
+ payload = {
59
+ "model": K2_MODEL,
60
+ "messages": messages,
61
+ "temperature": temperature,
62
+ "max_tokens": max_tokens,
63
+ "stream": stream
64
+ }
65
+
66
+ try:
67
+ response = requests.post(
68
+ self.base_url,
69
+ headers=self.headers,
70
+ json=payload,
71
+ timeout=120
72
+ )
73
+ response.raise_for_status()
74
+ result = response.json()
75
+
76
+ if 'choices' in result and len(result['choices']) > 0:
77
+ return result['choices'][0]['message']['content']
78
+ return f"Error: Unexpected response format: {result}"
79
+
80
+ except requests.exceptions.Timeout:
81
+ return "Error: Request timed out. K2 Think V2 API may be experiencing high load."
82
+ except requests.exceptions.RequestException as e:
83
+ return f"Error: API request failed - {str(e)}"
84
+
85
+ def analyze_market(self, ticker: str, data_summary: str,
86
+ technical_summary: str) -> str:
87
+ """Use K2 Think V2 to analyze market conditions"""
88
+ prompt = f"""You are an elite quantitative analyst at a top hedge fund (Jane Street / Two Sigma level).
89
+ Analyze the following financial data with deep reasoning and chain-of-thought analysis.
90
+
91
+ TICKER: {ticker}
92
+
93
+ MARKET DATA SUMMARY:
94
+ {data_summary}
95
+
96
+ TECHNICAL INDICATORS:
97
+ {technical_summary}
98
+
99
+ Please provide:
100
+ 1. **Executive Summary** β€” Key findings in 3 bullet points
101
+ 2. **Technical Analysis** β€” Interpret RSI, MACD, Bollinger Bands signals
102
+ 3. **Risk Assessment** β€” Volatility regime, tail risks, VaR estimation
103
+ 4. **Alpha Signal** β€” Directional bias (Bullish/Neutral/Bearish) with confidence %
104
+ 5. **Actionable Recommendation** β€” Specific trade idea with entry, stop-loss, target
105
+ 6. **Catalyst Watch** β€” What news/events could move this ticker next week
106
+
107
+ Think step-by-step and show your reasoning clearly."""
108
+
109
+ messages = [{"role": "user", "content": prompt}]
110
+ return self.chat(messages, temperature=0.3, max_tokens=4096)
111
+
112
+ def portfolio_advice(self, portfolio_data: str, risk_metrics: str) -> str:
113
+ """AI-powered portfolio optimization advice"""
114
+ prompt = f"""You are a portfolio manager at a quant hedge fund managing $500M AUM.
115
+ Provide institutional-grade portfolio analysis.
116
+
117
+ PORTFOLIO HOLDINGS:
118
+ {portfolio_data}
119
+
120
+ RISK METRICS:
121
+ {risk_metrics}
122
+
123
+ Please provide:
124
+ 1. **Portfolio Health Score** (0-100) and grade
125
+ 2. **Concentration Risk** β€” Are we over-exposed to any sector/style?
126
+ 3. **Correlation Analysis** β€” Hidden risks from correlated positions
127
+ 4. **Rebalancing Recommendation** β€” Specific weight adjustments with %
128
+ 5. **Hedging Strategy** β€” Options/ETFs to reduce tail risk
129
+ 6. **Expected Return & Sharpe** β€” Forward-looking estimate
130
+
131
+ Use quantitative reasoning. Reference specific numbers from the data."""
132
+
133
+ messages = [{"role": "user", "content": prompt}]
134
+ return self.chat(messages, temperature=0.3, max_tokens=4096)
135
+
136
+ def explain_strategy(self, strategy_name: str, metrics: str) -> str:
137
+ """Explain trading strategy performance with AI reasoning"""
138
+ prompt = f"""Explain this trading strategy's performance like you're teaching a quant interview candidate.
139
+
140
+ STRATEGY: {strategy_name}
141
+
142
+ PERFORMANCE METRICS:
143
+ {metrics}
144
+
145
+ Provide:
146
+ 1. **Strategy Intuition** β€” What economic/behavioral mechanism does it exploit?
147
+ 2. **Performance Attribution** β€” Break down P&L sources (alpha, beta, luck)
148
+ 3. **Risk-Adjusted Quality** β€” Sharpe, Sortino, max drawdown analysis
149
+ 4. **Benchmark Comparison** β€” How does it compare to buy-and-hold?
150
+ 5. **Edge Sustainability** β€” Will this alpha persist or decay?
151
+ 6. **Improvement Ideas** β€” 3 specific enhancements with expected impact
152
+
153
+ Be insightful and educational."""
154
+
155
+ messages = [{"role": "user", "content": prompt}]
156
+ return self.chat(messages, temperature=0.5, max_tokens=4096)
157
+
158
+
159
+ # ──────────────────────────────────────────────────────────────
160
+ # Market Data & Technical Analysis
161
+ # ──────────────────────────────────────────────────────────────
162
+ def fetch_stock_data(ticker: str, period: str = "6mo", interval: str = "1d"):
163
+ """Fetch stock data with error handling"""
164
+ try:
165
+ stock = yf.Ticker(ticker.upper().strip())
166
+ df = stock.history(period=period, interval=interval)
167
+
168
+ if df.empty:
169
+ return None, f"No data found for {ticker}. Please check the ticker symbol."
170
+
171
+ info = stock.info
172
+ return df, info
173
+ except Exception as e:
174
+ return None, f"Error fetching data: {str(e)}"
175
+
176
+
177
+ def compute_technical_indicators(df: pd.DataFrame) -> pd.DataFrame:
178
+ """Compute technical indicators"""
179
+ df = df.copy()
180
+
181
+ df['Returns'] = df['Close'].pct_change()
182
+ df['SMA_20'] = df['Close'].rolling(20).mean()
183
+ df['SMA_50'] = df['Close'].rolling(50).mean()
184
+ df['SMA_200'] = df['Close'].rolling(200).mean()
185
+ df['EMA_12'] = df['Close'].ewm(span=12, adjust=False).mean()
186
+ df['EMA_26'] = df['Close'].ewm(span=26, adjust=False).mean()
187
+ df['MACD'] = df['EMA_12'] - df['EMA_26']
188
+ df['MACD_Signal'] = df['MACD'].ewm(span=9, adjust=False).mean()
189
+ df['MACD_Histogram'] = df['MACD'] - df['MACD_Signal']
190
+
191
+ delta = df['Close'].diff()
192
+ gain = (delta.where(delta > 0, 0)).rolling(14).mean()
193
+ loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
194
+ rs = gain / (loss + 1e-10)
195
+ df['RSI'] = 100 - (100 / (1 + rs))
196
+
197
+ df['BB_Middle'] = df['Close'].rolling(20).mean()
198
+ bb_std = df['Close'].rolling(20).std()
199
+ df['BB_Upper'] = df['BB_Middle'] + 2 * bb_std
200
+ df['BB_Lower'] = df['BB_Middle'] - 2 * bb_std
201
+ df['BB_Width'] = (df['BB_Upper'] - df['BB_Lower']) / df['BB_Middle']
202
+ df['BB_Position'] = (df['Close'] - df['BB_Lower']) / (df['BB_Upper'] - df['BB_Lower'] + 1e-10)
203
+
204
+ typical_price = (df['High'] + df['Low'] + df['Close']) / 3
205
+ df['VWAP'] = (typical_price * df['Volume']).cumsum() / (df['Volume'].cumsum() + 1e-10)
206
+
207
+ high_low = df['High'] - df['Low']
208
+ high_close = np.abs(df['High'] - df['Close'].shift())
209
+ low_close = np.abs(df['Low'] - df['Close'].shift())
210
+ tr = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)
211
+ df['ATR'] = tr.rolling(14).mean()
212
+
213
+ low_14 = df['Low'].rolling(14).min()
214
+ high_14 = df['High'].rolling(14).max()
215
+ df['Stoch_K'] = 100 * (df['Close'] - low_14) / (high_14 - low_14 + 1e-10)
216
+ df['Stoch_D'] = df['Stoch_K'].rolling(3).mean()
217
+
218
+ df['Volume_MA'] = df['Volume'].rolling(20).mean()
219
+ df['Volume_Ratio'] = df['Volume'] / (df['Volume_MA'] + 1e-10)
220
+
221
+ return df
222
+
223
+
224
+ def generate_signals(df: pd.DataFrame) -> dict:
225
+ """Generate alpha signals from technical indicators"""
226
+ latest = df.iloc[-1]
227
+ prev = df.iloc[-2] if len(df) > 1 else latest
228
+
229
+ signals = {
230
+ 'trend': 'neutral',
231
+ 'momentum': 'neutral',
232
+ 'volatility': 'normal',
233
+ 'volume': 'normal',
234
+ 'composite_score': 50,
235
+ 'confidence': 50
236
+ }
237
+
238
+ if latest['Close'] > latest['SMA_20'] > latest['SMA_50']:
239
+ signals['trend'] = 'bullish'
240
+ elif latest['Close'] < latest['SMA_20'] < latest['SMA_50']:
241
+ signals['trend'] = 'bearish'
242
+
243
+ if latest['RSI'] < 30:
244
+ signals['momentum'] = 'oversold (bullish bounce potential)'
245
+ elif latest['RSI'] > 70:
246
+ signals['momentum'] = 'overbought (bearish pullback risk)'
247
+ elif latest['MACD'] > latest['MACD_Signal'] and prev['MACD'] <= prev['MACD_Signal']:
248
+ signals['momentum'] = 'MACD bullish crossover'
249
+ elif latest['MACD'] < latest['MACD_Signal'] and prev['MACD'] >= prev['MACD_Signal']:
250
+ signals['momentum'] = 'MACD bearish crossover'
251
+
252
+ bb_width = latest['BB_Width']
253
+ if bb_width > df['BB_Width'].quantile(0.9):
254
+ signals['volatility'] = 'expanding (breakout likely)'
255
+ elif bb_width < df['BB_Width'].quantile(0.1):
256
+ signals['volatility'] = 'contracting (squeeze setup)'
257
+
258
+ if latest['Volume_Ratio'] > 2.0:
259
+ signals['volume'] = 'heavy (institutional interest)'
260
+
261
+ score = 50
262
+ if signals['trend'] == 'bullish': score += 15
263
+ if signals['trend'] == 'bearish': score -= 15
264
+ if 'oversold' in signals['momentum']: score += 10
265
+ if 'overbought' in signals['momentum']: score -= 10
266
+ if 'bullish crossover' in signals['momentum']: score += 10
267
+ if 'bearish crossover' in signals['momentum']: score -= 10
268
+ if latest['Close'] > latest['VWAP']: score += 5
269
+ if latest['Close'] < latest['VWAP']: score -= 5
270
+
271
+ signals['composite_score'] = max(0, min(100, score))
272
+
273
+ bullish_count = sum([
274
+ signals['trend'] == 'bullish',
275
+ 'oversold' in signals['momentum'] or 'bullish crossover' in signals['momentum'],
276
+ latest['Close'] > latest['VWAP']
277
+ ])
278
+ bearish_count = sum([
279
+ signals['trend'] == 'bearish',
280
+ 'overbought' in signals['momentum'] or 'bearish crossover' in signals['momentum'],
281
+ latest['Close'] < latest['VWAP']
282
+ ])
283
+
284
+ signals['confidence'] = max(bullish_count, bearish_count) * 33 + 1
285
+ signals['direction'] = 'BULLISH' if score > 60 else 'BEARISH' if score < 40 else 'NEUTRAL'
286
+
287
+ return signals
288
+
289
+
290
+ def compute_risk_metrics(df: pd.DataFrame) -> dict:
291
+ """Compute portfolio risk metrics"""
292
+ returns = df['Returns'].dropna()
293
+
294
+ if len(returns) < 30:
295
+ return {}
296
+
297
+ annual_return = returns.mean() * 252
298
+ annual_vol = returns.std() * np.sqrt(252)
299
+ sharpe = annual_return / (annual_vol + 1e-10)
300
+
301
+ downside = returns[returns < 0]
302
+ downside_dev = downside.std() * np.sqrt(252) if len(downside) > 0 else 1e-10
303
+ sortino = annual_return / (downside_dev + 1e-10)
304
+
305
+ cumulative = (1 + returns).cumprod()
306
+ running_max = cumulative.expanding().max()
307
+ drawdown = (cumulative - running_max) / running_max
308
+ max_dd = drawdown.min()
309
+
310
+ var_95 = np.percentile(returns, 5)
311
+ var_99 = np.percentile(returns, 1)
312
+ cvar_95 = returns[returns <= var_95].mean() if len(returns[returns <= var_95]) > 0 else var_95
313
+
314
+ calmar = annual_return / (abs(max_dd) + 1e-10)
315
+ skew = returns.skew()
316
+ kurt = returns.kurtosis()
317
+
318
+ return {
319
+ 'annual_return': annual_return,
320
+ 'annual_volatility': annual_vol,
321
+ 'sharpe_ratio': sharpe,
322
+ 'sortino_ratio': sortino,
323
+ 'max_drawdown': max_dd,
324
+ 'var_95_daily': var_95,
325
+ 'var_99_daily': var_99,
326
+ 'cvar_95_daily': cvar_95,
327
+ 'calmar_ratio': calmar,
328
+ 'skewness': skew,
329
+ 'excess_kurtosis': kurt,
330
+ 'win_rate': (returns > 0).mean(),
331
+ 'avg_win': returns[returns > 0].mean() if len(returns[returns > 0]) > 0 else 0,
332
+ 'avg_loss': returns[returns < 0].mean() if len(returns[returns < 0]) > 0 else 0,
333
+ 'profit_factor': abs(returns[returns > 0].sum() / (returns[returns < 0].sum() + 1e-10))
334
+ }
335
+
336
+
337
+ # ──────────────────────────────────────────────────────────────
338
+ # Plotly Charts
339
+ # ──────────────────────────────────────────────────────────────
340
+ def create_candlestick_chart(df: pd.DataFrame, ticker: str):
341
+ """Create interactive candlestick chart with indicators"""
342
+ fig = make_subplots(
343
+ rows=3, cols=1,
344
+ shared_xaxes=True,
345
+ vertical_spacing=0.05,
346
+ row_heights=[0.6, 0.2, 0.2],
347
+ subplot_titles=(f'{ticker} Price', 'Volume', 'RSI')
348
+ )
349
+
350
+ fig.add_trace(go.Candlestick(
351
+ x=df.index,
352
+ open=df['Open'],
353
+ high=df['High'],
354
+ low=df['Low'],
355
+ close=df['Close'],
356
+ name='Price'
357
+ ), row=1, col=1)
358
+
359
+ fig.add_trace(go.Scatter(x=df.index, y=df['SMA_20'],
360
+ line=dict(color='orange', width=1), name='SMA 20'), row=1, col=1)
361
+ fig.add_trace(go.Scatter(x=df.index, y=df['SMA_50'],
362
+ line=dict(color='blue', width=1), name='SMA 50'), row=1, col=1)
363
+
364
+ fig.add_trace(go.Scatter(x=df.index, y=df['BB_Upper'],
365
+ line=dict(color='gray', width=1, dash='dash'),
366
+ name='BB Upper', opacity=0.5), row=1, col=1)
367
+ fig.add_trace(go.Scatter(x=df.index, y=df['BB_Lower'],
368
+ line=dict(color='gray', width=1, dash='dash'),
369
+ name='BB Lower', opacity=0.5), row=1, col=1)
370
+
371
+ colors = ['green' if df['Close'].iloc[i] >= df['Open'].iloc[i] else 'red'
372
+ for i in range(len(df))]
373
+ fig.add_trace(go.Bar(x=df.index, y=df['Volume'],
374
+ marker_color=colors, name='Volume', opacity=0.7), row=2, col=1)
375
+
376
+ fig.add_trace(go.Scatter(x=df.index, y=df['RSI'],
377
+ line=dict(color='purple', width=1.5), name='RSI'), row=3, col=1)
378
+ fig.add_hline(y=70, line_dash="dash", line_color="red", row=3, col=1)
379
+ fig.add_hline(y=30, line_dash="dash", line_color="green", row=3, col=1)
380
+ fig.add_hline(y=50, line_dash="dot", line_color="gray", row=3, col=1)
381
+
382
+ fig.update_layout(
383
+ title=f'{ticker} Technical Analysis Dashboard',
384
+ xaxis_rangeslider_visible=False,
385
+ height=800,
386
+ template='plotly_white',
387
+ showlegend=True,
388
+ legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1)
389
+ )
390
+
391
+ fig.update_yaxes(title_text="Price ($)", row=1, col=1)
392
+ fig.update_yaxes(title_text="Volume", row=2, col=1)
393
+ fig.update_yaxes(title_text="RSI", range=[0, 100], row=3, col=1)
394
+
395
+ return fig
396
+
397
+
398
+ def create_macd_chart(df: pd.DataFrame, ticker: str):
399
+ """Create MACD chart"""
400
+ fig = make_subplots(rows=1, cols=1)
401
+
402
+ fig.add_trace(go.Scatter(x=df.index, y=df['MACD'],
403
+ line=dict(color='blue', width=1.5), name='MACD'), row=1, col=1)
404
+ fig.add_trace(go.Scatter(x=df.index, y=df['MACD_Signal'],
405
+ line=dict(color='orange', width=1.5), name='Signal'), row=1, col=1)
406
+
407
+ colors = ['green' if val >= 0 else 'red' for val in df['MACD_Histogram']]
408
+ fig.add_trace(go.Bar(x=df.index, y=df['MACD_Histogram'],
409
+ marker_color=colors, name='Histogram', opacity=0.6), row=1, col=1)
410
+
411
+ fig.update_layout(
412
+ title=f'{ticker} MACD',
413
+ height=400,
414
+ template='plotly_white',
415
+ xaxis_rangeslider_visible=False
416
+ )
417
+
418
+ return fig
419
+
420
+
421
+ def create_return_distribution(returns: pd.Series, ticker: str):
422
+ """Create return distribution histogram"""
423
+ fig = go.Figure()
424
+
425
+ fig.add_trace(go.Histogram(
426
+ x=returns,
427
+ nbinsx=50,
428
+ name='Returns',
429
+ marker_color='steelblue',
430
+ opacity=0.7
431
+ ))
432
+
433
+ x_range = np.linspace(returns.min(), returns.max(), 100)
434
+ mu, sigma = returns.mean(), returns.std()
435
+ normal_pdf = len(returns) * (x_range[1] - x_range[0]) * stats.norm.pdf(x_range, mu, sigma)
436
+ fig.add_trace(go.Scatter(x=x_range, y=normal_pdf,
437
+ mode='lines', line=dict(color='red', dash='dash'),
438
+ name='Normal'))
439
+
440
+ fig.update_layout(
441
+ title=f'{ticker} Return Distribution (vs Normal)',
442
+ xaxis_title='Daily Return',
443
+ yaxis_title='Frequency',
444
+ height=400,
445
+ template='plotly_white',
446
+ bargap=0.1
447
+ )
448
+
449
+ return fig
450
+
451
+
452
+ # ──────────────────────────────────────────────────────────────
453
+ # Portfolio Optimization
454
+ # ──────────────────────────────────────────────────────────────
455
+ def optimize_portfolio(tickers: list, period: str = "1y"):
456
+ """Mean-variance optimization for a portfolio"""
457
+ try:
458
+ data = {}
459
+ for t in tickers:
460
+ t = t.strip().upper()
461
+ if not t:
462
+ continue
463
+ df, _ = fetch_stock_data(t, period=period)
464
+ if df is not None and len(df) > 30:
465
+ data[t] = df['Close']
466
+
467
+ if len(data) < 2:
468
+ return None, "Need at least 2 valid tickers for portfolio optimization."
469
+
470
+ prices = pd.DataFrame(data)
471
+ prices = prices.dropna()
472
+ returns = prices.pct_change().dropna()
473
+
474
+ if len(returns) < 30:
475
+ return None, "Insufficient data after alignment."
476
+
477
+ mu = returns.mean() * 252
478
+ sigma = returns.cov() * 252
479
+ n = len(mu)
480
+
481
+ def neg_sharpe(weights):
482
+ weights = np.array(weights)
483
+ port_return = np.dot(weights, mu)
484
+ port_vol = np.sqrt(np.dot(weights.T, np.dot(sigma, weights)))
485
+ return -(port_return / (port_vol + 1e-10))
486
+
487
+ constraints = {'type': 'eq', 'fun': lambda w: np.sum(w) - 1}
488
+ bounds = tuple((0, 0.5) for _ in range(n))
489
+ x0 = np.ones(n) / n
490
+
491
+ result = minimize(neg_sharpe, x0, method='SLSQP', bounds=bounds, constraints=constraints)
492
+ optimal_weights = result.x
493
+
494
+ port_return = np.dot(optimal_weights, mu)
495
+ port_vol = np.sqrt(np.dot(optimal_weights.T, np.dot(sigma, optimal_weights)))
496
+ port_sharpe = port_return / (port_vol + 1e-10)
497
+
498
+ eq_weights = np.ones(n) / n
499
+ eq_return = np.dot(eq_weights, mu)
500
+ eq_vol = np.sqrt(np.dot(eq_weights.T, np.dot(sigma, eq_weights)))
501
+ eq_sharpe = eq_return / (eq_vol + 1e-10)
502
+
503
+ return {
504
+ 'tickers': list(data.keys()),
505
+ 'optimal_weights': optimal_weights,
506
+ 'equal_weights': eq_weights,
507
+ 'optimal_return': port_return,
508
+ 'optimal_volatility': port_vol,
509
+ 'optimal_sharpe': port_sharpe,
510
+ 'equal_return': eq_return,
511
+ 'equal_volatility': eq_vol,
512
+ 'equal_sharpe': eq_sharpe,
513
+ 'annual_returns': mu,
514
+ 'covariance': sigma
515
+ }, None
516
+
517
+ except Exception as e:
518
+ return None, f"Optimization error: {str(e)}"
519
+
520
+
521
+ def create_efficient_frontier(opt_result: dict):
522
+ """Plot efficient frontier"""
523
+ mu = opt_result['annual_returns']
524
+ sigma = opt_result['covariance']
525
+ n = len(mu)
526
+
527
+ n_portfolios = 5000
528
+ weights = np.random.dirichlet(np.ones(n), n_portfolios)
529
+
530
+ port_returns = np.dot(weights, mu)
531
+ port_vols = np.array([np.sqrt(np.dot(w.T, np.dot(sigma, w))) for w in weights])
532
+ port_sharpes = port_returns / (port_vols + 1e-10)
533
+
534
+ fig = go.Figure()
535
+
536
+ fig.add_trace(go.Scatter(
537
+ x=port_vols, y=port_returns,
538
+ mode='markers',
539
+ marker=dict(
540
+ size=4,
541
+ color=port_sharpes,
542
+ colorscale='Viridis',
543
+ showscale=True,
544
+ colorbar=dict(title='Sharpe')
545
+ ),
546
+ name='Random Portfolios'
547
+ ))
548
+
549
+ fig.add_trace(go.Scatter(
550
+ x=[opt_result['optimal_volatility']],
551
+ y=[opt_result['optimal_return']],
552
+ mode='markers+text',
553
+ marker=dict(size=15, color='red', symbol='star'),
554
+ text=['Optimal'],
555
+ textposition='top center',
556
+ name='Optimal Portfolio'
557
+ ))
558
+
559
+ fig.add_trace(go.Scatter(
560
+ x=[opt_result['equal_volatility']],
561
+ y=[opt_result['equal_return']],
562
+ mode='markers+text',
563
+ marker=dict(size=12, color='orange', symbol='diamond'),
564
+ text=['Equal Weight'],
565
+ textposition='bottom center',
566
+ name='Equal Weight'
567
+ ))
568
+
569
+ fig.update_layout(
570
+ title='Efficient Frontier (Monte Carlo Simulation)',
571
+ xaxis_title='Annual Volatility',
572
+ yaxis_title='Annual Return',
573
+ template='plotly_white',
574
+ height=500
575
+ )
576
+
577
+ return fig
578
+
579
+
580
+ # ──────────────────────────────────────────────────────────────
581
+ # Gradio UI Functions
582
+ # ──────────────────────────────────────────────────────────────
583
+ def analyze_ticker(ticker: str, period: str = "6mo"):
584
+ """Main analysis function for single ticker"""
585
+ ticker = ticker.strip().upper()
586
+ if not ticker:
587
+ return None, None, None, None, "Please enter a ticker symbol."
588
+
589
+ df, info = fetch_stock_data(ticker, period=period)
590
+ if df is None:
591
+ return None, None, None, None, info
592
+
593
+ df = compute_technical_indicators(df)
594
+ signals = generate_signals(df)
595
+ risk = compute_risk_metrics(df)
596
+
597
+ candlestick = create_candlestick_chart(df, ticker)
598
+ macd_chart = create_macd_chart(df, ticker)
599
+ returns_chart = create_return_distribution(df['Returns'].dropna(), ticker)
600
+
601
+ latest = df.iloc[-1]
602
+ prev = df.iloc[-2] if len(df) > 1 else latest
603
+
604
+ summary = f"""## {ticker} Analysis
605
+
606
+ **Current Price:** ${latest['Close']:.2f} | **Change:** {((latest['Close']/prev['Close']-1)*100):+.2f}%
607
+
608
+ ### Technical Signals
609
+ | Indicator | Value | Signal |
610
+ |-----------|-------|--------|
611
+ | RSI (14) | {latest['RSI']:.1f} | {'Oversold' if latest['RSI']<30 else 'Overbought' if latest['RSI']>70 else 'Neutral'} |
612
+ | MACD | {latest['MACD']:.3f} | {'Bullish' if latest['MACD']>latest['MACD_Signal'] else 'Bearish'} |
613
+ | Bollinger Position | {latest['BB_Position']:.1%} | {'Near upper band' if latest['BB_Position']>0.8 else 'Near lower band' if latest['BB_Position']<0.2 else 'Middle'} |
614
+ | VWAP Position | {'Above VWAP' if latest['Close']>latest['VWAP'] else 'Below VWAP'} | {'Bullish' if latest['Close']>latest['VWAP'] else 'Bearish'} |
615
+ | Volume vs Avg | {latest['Volume_Ratio']:.1f}x | {'Heavy' if latest['Volume_Ratio']>1.5 else 'Normal'} |
616
+
617
+ ### Composite Signal
618
+ **Direction:** {signals['direction']} | **Score:** {signals['composite_score']}/100 | **Confidence:** {signals['confidence']}%
619
+
620
+ ### Risk Metrics
621
+ | Metric | Value |
622
+ |--------|-------|
623
+ | Annualized Return | {risk.get('annual_return', 0)*100:.1f}% |
624
+ | Annualized Volatility | {risk.get('annual_volatility', 0)*100:.1f}% |
625
+ | Sharpe Ratio | {risk.get('sharpe_ratio', 0):.2f} |
626
+ | Sortino Ratio | {risk.get('sortino_ratio', 0):.2f} |
627
+ | Max Drawdown | {risk.get('max_drawdown', 0)*100:.1f}% |
628
+ | VaR (95%) | {risk.get('var_95_daily', 0)*100:.2f}% |
629
+ | CVaR (95%) | {risk.get('cvar_95_daily', 0)*100:.2f}% |
630
+ | Calmar Ratio | {risk.get('calmar_ratio', 0):.2f} |
631
+ | Win Rate | {risk.get('win_rate', 0)*100:.1f}% |
632
+ | Profit Factor | {risk.get('profit_factor', 0):.2f} |
633
+ | Skewness | {risk.get('skewness', 0):.2f} |
634
+ | Excess Kurtosis | {risk.get('excess_kurtosis', 0):.2f} |
635
+ """
636
+
637
+ return candlestick, macd_chart, returns_chart, summary, ""
638
+
639
+
640
+ def ai_analyze(ticker: str, period: str = "6mo"):
641
+ """AI-powered analysis using K2 Think V2"""
642
+ ticker = ticker.strip().upper()
643
+ if not ticker:
644
+ return "Please enter a ticker symbol."
645
+
646
+ df, info = fetch_stock_data(ticker, period=period)
647
+ if df is None:
648
+ return info
649
+
650
+ df = compute_technical_indicators(df)
651
+ signals = generate_signals(df)
652
+ risk = compute_risk_metrics(df)
653
+ latest = df.iloc[-1]
654
+
655
+ data_summary = f"""
656
+ Ticker: {ticker}
657
+ Current Price: ${latest['Close']:.2f}
658
+ Period: {period}
659
+ Data Points: {len(df)}
660
+
661
+ Price Action:
662
+ - 20-day SMA: ${latest['SMA_20']:.2f}
663
+ - 50-day SMA: ${latest['SMA_50']:.2f}
664
+ - 52-week High: ${df['High'].max():.2f}
665
+ - 52-week Low: ${df['Low'].min():.2f}
666
+ - ATR (14): ${latest['ATR']:.2f}
667
+ """
668
+
669
+ technical_summary = f"""
670
+ RSI (14): {latest['RSI']:.1f} ({'oversold' if latest['RSI']<30 else 'overbought' if latest['RSI']>70 else 'neutral'})
671
+ MACD: {latest['MACD']:.3f} vs Signal: {latest['MACD_Signal']:.3f}
672
+ MACD Histogram: {latest['MACD_Histogram']:.3f}
673
+ Bollinger Band Position: {latest['BB_Position']:.1%}
674
+ Stochastic %K: {latest['Stoch_K']:.1f}
675
+ VWAP: ${latest['VWAP']:.2f}
676
+
677
+ Composite Signal Score: {signals['composite_score']}/100
678
+ Direction: {signals['direction']}
679
+ Confidence: {signals['confidence']}%
680
+
681
+ Risk Metrics:
682
+ Sharpe: {risk.get('sharpe_ratio', 0):.2f}
683
+ Volatility (ann): {risk.get('annual_volatility', 0)*100:.1f}%
684
+ Max Drawdown: {risk.get('max_drawdown', 0)*100:.1f}%
685
+ VaR 95%: {risk.get('var_95_daily', 0)*100:.2f}%
686
+ """
687
+
688
+ client = K2ThinkClient()
689
+ analysis = client.analyze_market(ticker, data_summary, technical_summary)
690
+
691
+ return analysis
692
+
693
+
694
+ def analyze_portfolio(tickers_str: str, period: str = "1y"):
695
+ """Portfolio optimization and analysis"""
696
+ tickers = [t.strip().upper() for t in tickers_str.split(',') if t.strip()]
697
+
698
+ if len(tickers) < 2:
699
+ return None, None, "Please enter at least 2 tickers separated by commas."
700
+
701
+ result, error = optimize_portfolio(tickers, period)
702
+
703
+ if error:
704
+ return None, None, error
705
+
706
+ frontier = create_efficient_frontier(result)
707
+
708
+ weights_df = pd.DataFrame({
709
+ 'Ticker': result['tickers'],
710
+ 'Optimal Weight (%)': result['optimal_weights'] * 100,
711
+ 'Equal Weight (%)': result['equal_weights'] * 100
712
+ })
713
+
714
+ summary = f"""## Portfolio Optimization Results
715
+
716
+ **Tickers:** {', '.join(result['tickers'])}
717
+
718
+ ### Optimal Portfolio (Max Sharpe)
719
+ | Metric | Value |
720
+ |--------|-------|
721
+ | Expected Annual Return | {result['optimal_return']*100:.1f}% |
722
+ | Expected Volatility | {result['optimal_volatility']*100:.1f}% |
723
+ | Sharpe Ratio | {result['optimal_sharpe']:.2f} |
724
+
725
+ ### Equal Weight Portfolio (Benchmark)
726
+ | Metric | Value |
727
+ |--------|-------|
728
+ | Expected Annual Return | {result['equal_return']*100:.1f}% |
729
+ | Expected Volatility | {result['equal_volatility']*100:.1f}% |
730
+ | Sharpe Ratio | {result['equal_sharpe']:.2f} |
731
+
732
+ ### Improvement
733
+ | | |
734
+ |-|-|
735
+ | Sharpe Improvement | {((result['optimal_sharpe']/result['equal_sharpe']-1)*100):+.1f}% |
736
+ | Return Improvement | {((result['optimal_return']/result['equal_return']-1)*100):+.1f}% |
737
+ | Risk Reduction | {((1-result['optimal_volatility']/result['equal_volatility'])*100):+.1f}% |
738
+
739
+ ### Optimal Weights
740
+ {weights_df.to_markdown(index=False)}
741
+ """
742
+
743
+ return frontier, weights_df, summary
744
+
745
+
746
+ def ai_portfolio_advice(tickers_str: str, period: str = "1y"):
747
+ """AI-powered portfolio advice via K2 Think V2"""
748
+ tickers = [t.strip().upper() for t in tickers_str.split(',') if t.strip()]
749
+
750
+ if len(tickers) < 2:
751
+ return "Please enter at least 2 tickers."
752
+
753
+ result, error = optimize_portfolio(tickers, period)
754
+ if error:
755
+ return error
756
+
757
+ portfolio_data = f"""
758
+ Holdings:
759
+ {chr(10).join([f"- {t}: {w*100:.1f}%" for t, w in zip(result['tickers'], result['optimal_weights'])])}
760
+
761
+ Expected Return: {result['optimal_return']*100:.1f}%
762
+ Expected Volatility: {result['optimal_volatility']*100:.1f}%
763
+ Sharpe Ratio: {result['optimal_sharpe']:.2f}
764
+
765
+ Individual Asset Returns (annual):
766
+ {chr(10).join([f"- {t}: {r*100:.1f}%" for t, r in zip(result['tickers'], result['annual_returns'])])}
767
+ """
768
+
769
+ corr = result['covariance']
770
+ for i in range(len(corr)):
771
+ corr.iloc[i, i] = np.nan
772
+
773
+ risk_metrics = f"""
774
+ Correlation Matrix (off-diagonal):
775
+ {corr.to_string()}
776
+
777
+ Highest pairwise correlation: {corr.stack().max():.2f}
778
+ Lowest pairwise correlation: {corr.stack().min():.2f}
779
+ Average correlation: {corr.stack().mean():.2f}
780
+ """
781
+
782
+ client = K2ThinkClient()
783
+ advice = client.portfolio_advice(portfolio_data, risk_metrics)
784
+
785
+ return advice
786
+
787
+
788
+ # ──────────────────────────────────────────────────────────────
789
+ # Gradio App Builder
790
+ # ──────────────────────────────────────────────────────────────
791
+ def build_app():
792
+ """Build the Gradio demo app"""
793
+
794
+ with gr.Blocks(
795
+ title="AlphaForge x K2 Think V2 β€” Elite Quant Trading",
796
+ theme=gr.themes.Soft(),
797
+ css="""
798
+ .main-title { text-align: center; font-size: 2.5em; font-weight: bold; color: #1a73e8; margin-bottom: 0.2em; }
799
+ .subtitle { text-align: center; font-size: 1.1em; color: #5f6368; margin-bottom: 1.5em; }
800
+ .api-badge { text-align: center; background: linear-gradient(90deg, #667eea, #764ba2);
801
+ color: white; padding: 8px 16px; border-radius: 20px; font-weight: 600; }
802
+ """
803
+ ) as demo:
804
+
805
+ gr.HTML("""
806
+ <div class="main-title">πŸ”₯ AlphaForge x K2 Think V2</div>
807
+ <div class="subtitle">Elite Quantitative Trading Platform powered by MBZUAI's State-of-the-Art Reasoning Model</div>
808
+ <div style="text-align: center; margin-bottom: 20px;">
809
+ <span class="api-badge">πŸ€– K2 Think V2 API Active</span>
810
+ <span style="margin-left: 10px;" class="api-badge">πŸ“Š Real-Time Market Data</span>
811
+ <span style="margin-left: 10px;" class="api-badge">🎯 AI-Powered Alpha</span>
812
+ </div>
813
+ """)
814
+
815
+ # ── TAB 1: Single Stock Analysis ──
816
+ with gr.Tab("πŸ“ˆ Single Stock Analysis"):
817
+ with gr.Row():
818
+ with gr.Column(scale=1):
819
+ ticker_input = gr.Textbox(
820
+ label="Stock Ticker",
821
+ placeholder="e.g., AAPL, TSLA, NVDA, SPY",
822
+ value="AAPL"
823
+ )
824
+ period_input = gr.Dropdown(
825
+ label="Time Period",
826
+ choices=["1mo", "3mo", "6mo", "1y", "2y", "5y"],
827
+ value="6mo"
828
+ )
829
+ analyze_btn = gr.Button("πŸ” Analyze Stock", variant="primary")
830
+ ai_btn = gr.Button("πŸ€– AI Deep Analysis (K2 Think V2)", variant="secondary")
831
+
832
+ with gr.Column(scale=2):
833
+ summary_output = gr.Markdown(label="Analysis Summary")
834
+
835
+ with gr.Row():
836
+ candlestick_plot = gr.Plot(label="Price & Technicals")
837
+ macd_plot = gr.Plot(label="MACD")
838
+
839
+ with gr.Row():
840
+ returns_plot = gr.Plot(label="Return Distribution")
841
+ ai_output = gr.Textbox(
842
+ label="K2 Think V2 AI Analysis",
843
+ lines=20,
844
+ max_lines=30,
845
+ autoscroll=True
846
+ )
847
+
848
+ error_output = gr.Textbox(label="Status", visible=False)
849
+
850
+ analyze_btn.click(
851
+ fn=analyze_ticker,
852
+ inputs=[ticker_input, period_input],
853
+ outputs=[candlestick_plot, macd_plot, returns_plot, summary_output, error_output]
854
+ )
855
+
856
+ ai_btn.click(
857
+ fn=ai_analyze,
858
+ inputs=[ticker_input, period_input],
859
+ outputs=[ai_output]
860
+ )
861
+
862
+ # ── TAB 2: Portfolio Optimization ──
863
+ with gr.Tab("πŸ’Ό Portfolio Optimizer"):
864
+ with gr.Row():
865
+ with gr.Column(scale=1):
866
+ portfolio_input = gr.Textbox(
867
+ label="Portfolio Tickers (comma-separated)",
868
+ placeholder="e.g., AAPL, MSFT, GOOGL, AMZN, NVDA",
869
+ value="AAPL, MSFT, GOOGL, AMZN, NVDA"
870
+ )
871
+ portfolio_period = gr.Dropdown(
872
+ label="Lookback Period",
873
+ choices=["6mo", "1y", "2y", "3y"],
874
+ value="1y"
875
+ )
876
+ optimize_btn = gr.Button("🎯 Optimize Portfolio", variant="primary")
877
+ ai_portfolio_btn = gr.Button("πŸ€– AI Portfolio Advice (K2 Think V2)", variant="secondary")
878
+
879
+ with gr.Column(scale=2):
880
+ portfolio_summary = gr.Markdown(label="Optimization Results")
881
+
882
+ with gr.Row():
883
+ frontier_plot = gr.Plot(label="Efficient Frontier")
884
+ weights_table = gr.DataFrame(label="Optimal Weights")
885
+
886
+ ai_portfolio_output = gr.Textbox(
887
+ label="K2 Think V2 Portfolio Advice",
888
+ lines=20,
889
+ max_lines=30
890
+ )
891
+
892
+ optimize_btn.click(
893
+ fn=analyze_portfolio,
894
+ inputs=[portfolio_input, portfolio_period],
895
+ outputs=[frontier_plot, weights_table, portfolio_summary]
896
+ )
897
+
898
+ ai_portfolio_btn.click(
899
+ fn=ai_portfolio_advice,
900
+ inputs=[portfolio_input, portfolio_period],
901
+ outputs=[ai_portfolio_output]
902
+ )
903
+
904
+ # ── TAB 3: AI Chat ──
905
+ with gr.Tab("πŸ’¬ K2 Think V2 Chat"):
906
+ gr.Markdown("## Direct Chat with K2 Think V2\nAsk any financial question, strategy idea, or market analysis.")
907
+
908
+ chat_input = gr.Textbox(
909
+ label="Your Question",
910
+ placeholder="e.g., 'Analyze the bull case for AI stocks in 2025' or 'Explain pairs trading with a real example'",
911
+ lines=3
912
+ )
913
+ chat_temp = gr.Slider(label="Temperature", minimum=0.0, maximum=1.0, value=0.5, step=0.1)
914
+ chat_btn = gr.Button("πŸš€ Ask K2 Think V2", variant="primary")
915
+ chat_output = gr.Textbox(label="K2 Think V2 Response", lines=25, max_lines=40)
916
+
917
+ def direct_chat(question, temperature):
918
+ if not question.strip():
919
+ return "Please enter a question."
920
+ client = K2ThinkClient()
921
+ messages = [{"role": "user", "content": question}]
922
+ return client.chat(messages, temperature=temperature, max_tokens=4096)
923
+
924
+ chat_btn.click(
925
+ fn=direct_chat,
926
+ inputs=[chat_input, chat_temp],
927
+ outputs=[chat_output]
928
+ )
929
+
930
+ # ── TAB 4: About ──
931
+ with gr.Tab("ℹ️ About"):
932
+ gr.Markdown("""
933
+ ## AlphaForge x K2 Think V2
934
+
935
+ Built for the **Build with K2 Think V2 Challenge** by MBZUAI.
936
+
937
+ ### What This Demo Shows
938
+
939
+ 1. **Real-Time Market Data** β€” Fetch live stock prices, volume, OHLCV from Yahoo Finance
940
+ 2. **Technical Analysis** β€” 14+ indicators: RSI, MACD, Bollinger, VWAP, Stochastic, ATR
941
+ 3. **Alpha Signal Generation** β€” Composite scoring combining trend, momentum, volatility, volume
942
+ 4. **Risk Metrics** β€” Sharpe, Sortino, VaR, CVaR, Calmar, max drawdown, skewness, kurtosis
943
+ 5. **Portfolio Optimization** β€” Mean-variance optimization with efficient frontier visualization
944
+ 6. **AI-Powered Analysis** β€” K2 Think V2 (MBZUAI's SOTA reasoning model) provides deep chain-of-thought analysis
945
+
946
+ ### Architecture
947
+
948
+ ```
949
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
950
+ β”‚ yfinance │────▢│ AlphaForge │────▢│ K2 Think V2 β”‚
951
+ β”‚ (market data) β”‚ β”‚ (quant signals) β”‚ β”‚ (AI reasoning) β”‚
952
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
953
+ ```
954
+
955
+ ### K2 Think V2 API
956
+
957
+ - **Model**: MBZUAI-IFM/K2-Think-v2
958
+ - **Provider**: MBZUAI (Mohamed bin Zayed University of AI)
959
+ - **Features**: Chain-of-thought reasoning, deep financial analysis, multi-step logic
960
+
961
+ ### Technologies
962
+
963
+ - **Gradio** β€” Interactive web UI
964
+ - **yfinance** β€” Real-time market data
965
+ - **Plotly** β€” Interactive charts
966
+ - **K2 Think V2** β€” AI reasoning and analysis
967
+
968
+ ### Links
969
+
970
+ - [AlphaForge Quant System](https://huggingface.co/Premchan369/alphaforge-quant-system) β€” Full open-source quant platform (25 modules)
971
+ - [Build with K2 Think V2](https://build.k2think.ai/) β€” Official challenge page
972
+ - [MBZUAI](https://mbzuai.ac.ae/) β€” Mohamed bin Zayed University of AI
973
+
974
+ ---
975
+
976
+ *Built by Premchan | Built with K2 Think V2 Challenge*
977
+ """)
978
+
979
+ return demo
980
+
981
+
982
+ # ──────────────────────────────────────────────────────────────
983
+ # Main Entry Point
984
+ # ──────────────────────────────────────────────────────────────
985
+ if __name__ == "__main__":
986
+ demo = build_app()
987
+
988
+ demo.queue().launch(
989
+ server_name="0.0.0.0",
990
+ server_port=7860,
991
+ share=True,
992
+ show_error=True
993
+ )