Yash3786's picture
Update main.py
6825bc3 verified
Raw
History Blame Contribute Delete
14.7 kB
import os
import json
import time
import asyncio
import requests
import pandas as pd
import yfinance as yf
from fastapi import FastAPI, HTTPException, Query
from fastapi.middleware.cors import CORSMiddleware
import google.generativeai as genai
from groq import Groq
from datetime import datetime, timedelta
import pytz
# =================================================================
# CONFIGURATION & KEYS (EXACTLY AS REQUESTED)
# =================================================================
GEMINI_KEY = "AIzaSyD3frbI4K0gsNq6azH-jNGqHoqCzmH8jnE"
GROQ_KEY = "gsk_APdDBgydECWVAdbFAOi7WGdyb3FYUZmiSqYkcS8sjiaqOXNDU21tq"
genai.configure(api_key=GEMINI_KEY)
groq_client = Groq(api_key=GROQ_KEY)
app = FastAPI(title="Vantage Ultra-Prism Pro API", version="4.0.0")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# =================================================================
# AGGRESSIVE CACHING UTILITY
# =================================================================
cache_store = {}
def get_cache(key: str, ttl: int):
"""
ttl in seconds:
- Option Chain: 60s
- Technicals: 30s
- Prices: 5s
- AI Strategy: 120s
"""
if key in cache_store:
data, timestamp = cache_store[key]
if time.time() - timestamp < ttl:
return data
return None
def set_cache(key: str, data: any):
cache_store[key] = (data, time.time())
# =================================================================
# MARKET STATUS UTILITY
# =================================================================
def get_market_status():
ist = pytz.timezone('Asia/Kolkata')
now = datetime.now(ist)
# Check if Weekend
if now.weekday() >= 5:
return False, "Market Closed (Weekend)"
# Check Market Hours (9:15 AM to 3:30 PM)
start_time = now.replace(hour=9, minute=15, second=0, microsecond=0)
end_time = now.replace(hour=15, minute=30, second=0, microsecond=0)
if start_time <= now <= end_time:
return True, "Market Open"
elif now < start_time:
return False, f"Market Opens at 09:15 AM"
else:
return False, "Market Closed"
# =================================================================
# NSE SESSION MANAGEMENT
# =================================================================
class NSESession:
def __init__(self):
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
'Accept': '*/*',
'Accept-Language': 'en-US,en;q=0.9',
'Referer': 'https://www.nseindia.com/option-chain',
}
self.session = requests.Session()
self.session.headers.update(self.headers)
self.last_init = 0
def init_session(self):
if time.time() - self.last_init > 300: # Refresh every 5 mins
try:
self.session.get("https://www.nseindia.com", timeout=10)
# Also hit option chain page to get cookies
self.session.get("https://www.nseindia.com/option-chain", timeout=10)
self.last_init = time.time()
except Exception as e:
print(f"NSE Session Error: {e}")
def get_json(self, url: str):
self.init_session()
try:
response = self.session.get(url, timeout=8)
if response.status_code == 200:
return response.json()
elif response.status_code == 401:
self.last_init = 0 # Force re-init
return self.get_json(url)
except Exception as e:
print(f"API Fetch Error: {e}")
return None
nse_client = NSESession()
# =================================================================
# DATA PROCESSING HELPERS
# =================================================================
def get_historical_data(symbol: str, period="5d", interval="5m"):
cache_key = f"hist_{symbol}_{period}_{interval}"
cached = get_cache(cache_key, 30) # Internal buffer
if cached is not None: return cached
try:
df = yf.download(symbol, period=period, interval=interval, progress=False)
if df.empty:
df = yf.download(symbol, period="1mo", interval="1d", progress=False)
if not df.empty:
set_cache(cache_key, df)
return df
except:
pass
return None
def calculate_technicals(df: pd.DataFrame):
if df is None or df.empty:
return {}
try:
# Extract series
close = df['Close'].iloc[:, 0] if isinstance(df['Close'], pd.DataFrame) else df['Close']
high = df['High'].iloc[:, 0] if isinstance(df['High'], pd.DataFrame) else df['High']
low = df['Low'].iloc[:, 0] if isinstance(df['Low'], pd.DataFrame) else df['Low']
current_price = float(close.iloc[-1])
prev_close = float(close.iloc[-2]) if len(close) > 1 else current_price
# RSI (14)
delta = close.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
rs = gain / (loss + 1e-9)
rsi = 100 - (100 / (1 + rs))
current_rsi = round(float(rsi.iloc[-1]), 2) if not rsi.empty else 50
# EMA (20)
ema20 = close.ewm(span=20, adjust=False).mean().iloc[-1]
# Support/Resistance (Pivot)
max_h = float(high.max())
min_l = float(low.min())
pivot = (max_h + min_l + current_price) / 3
r1 = (2 * pivot) - min_l
s1 = (2 * pivot) - max_h
return {
"price": round(current_price, 2),
"change": round(current_price - prev_close, 2),
"changePercent": round(((current_price - prev_close) / prev_close) * 100, 2),
"rsi": current_rsi,
"ema20": round(float(ema20), 2),
"pivot": round(pivot, 2),
"r1": round(r1, 2),
"s1": round(s1, 2),
"trend": "Bullish" if current_rsi > 55 else "Bearish" if current_rsi < 45 else "Neutral",
"strength": "Strong" if abs(current_rsi - 50) > 15 else "Weak"
}
except Exception as e:
print(f"Technical Error: {e}")
return {}
# =================================================================
# AI STRATEGY ENGINE
# =================================================================
async def generate_fno_strategy(symbol: str, techs: dict, oc_data: dict = None):
cache_key = f"ai_{symbol}"
cached = get_cache(cache_key, 120)
if cached: return cached
prompt = f"""
ROLE: Institutional F&O Quant Researcher
TASK: Generate a high-conviction intraday/F&O strategy for {symbol}.
TECHNICALS:
Price: ₹{techs.get('price')}
Trend: {techs.get('trend')} ({techs.get('strength')})
RSI: {techs.get('rsi')}
Pivot: {techs.get('pivot')}
Levels: R1={techs.get('r1')}, S1={techs.get('s1')}
OPTION DATA (PCR/OI):
{oc_data if oc_data else 'No data - Use Technicals'}
Output exactly ONE JSON object in a list. DO NOT include markdown formatting or extra text.
Schema:
[{{
"stockName": "{symbol} Pro Analysis",
"symbol": "{symbol}",
"tradeDate": "{datetime.now().strftime('%d %b %Y')}",
"isMarketOpen": true,
"tradeType": "Intraday F&O",
"prevClose": {techs.get('price', 0) - techs.get('change', 0)},
"openPrice": {techs.get('price', 0)},
"currentMarketPrice": {techs.get('price', 0)},
"exactEntryPrice": <float>,
"exactEntryTime": "ASAP / On Breakout",
"target1": <float>,
"target2": <float>,
"stopLoss": <float>,
"estimatedProfitPercentage": <float>,
"estimatedRiskPercentage": <float>,
"riskLevel": "Low/Medium/High",
"strategyLogic": "Detailed Quant analysis explanation..."
}}]
"""
# Try Gemini First
try:
model = genai.GenerativeModel('gemini-1.5-flash')
response = await asyncio.to_thread(model.generate_content, prompt)
res_text = response.text.strip()
if "```json" in res_text:
res_text = res_text.split("```json")[1].split("```")[0].strip()
elif "```" in res_text:
res_text = res_text.split("```")[1].strip()
data = json.loads(res_text)
set_cache(cache_key, data)
return data
except:
# Fallback to Groq
try:
completion = groq_client.chat.completions.create(
model="llama-3.3-70b-versatile",
messages=[{"role": "user", "content": prompt}]
)
res_text = completion.choices[0].message.content.strip()
if "```json" in res_text:
res_text = res_text.split("```json")[1].split("```")[0].strip()
data = json.loads(res_text)
set_cache(cache_key, data)
return data
except:
return [{"stockName": symbol, "strategyLogic": "AI Engine busy. Follow Technical Levels."}]
# =================================================================
# API ENDPOINTS
# =================================================================
@app.get("/api/trade")
async def get_trade(symbol: str = "NIFTY"):
""" High-Speed AI Strategy Endpoint """
yf_map = {"NIFTY": "^NSEI", "BANKNIFTY": "^NSEBANK", "FINNIFTY": "NIFTY_FIN_SERVICE.NS"}
yf_sym = yf_map.get(symbol.upper(), f"{symbol.upper()}.NS")
df = get_historical_data(yf_sym)
techs = calculate_technicals(df)
if not techs:
raise HTTPException(status_code=404, detail="Symbol Data Error")
# Try to get OC summary for better AI input
oc_summary = None
if symbol.upper() in ["NIFTY", "BANKNIFTY", "FINNIFTY"]:
try:
oc = await get_option_chain(symbol)
oc_summary = {"pcr": oc.get("pcr"), "max_pain": "High Volume Zone"}
except: pass
return await generate_fno_strategy(symbol, techs, oc_summary)
@app.get("/api/option-chain")
async def get_option_chain(symbol: str = "NIFTY"):
""" Professional F&O Option Chain Endpoint """
cache_key = f"oc_{symbol.upper()}"
cached = get_cache(cache_key, 60)
if cached: return cached
base_url = "https://www.nseindia.com/api/option-chain-"
url = f"{base_url}indices?symbol={symbol.upper()}"
if symbol.upper() not in ["NIFTY", "BANKNIFTY", "FINNIFTY", "MIDCPNIFTY"]:
url = f"{base_url}equities?symbol={symbol.upper()}"
data = nse_client.get_json(url)
if not data:
# Fallback Mock/Simulated for Stability
return {"symbol": symbol, "error": "NSE Latency. Use Technicals.", "status": "Degraded"}
try:
records = data.get('records', {})
filtered = data.get('filtered', {})
ce_oi = filtered.get('CE', {}).get('totOI', 0)
pe_oi = filtered.get('PE', {}).get('totOI', 0)
pcr = round(pe_oi / (ce_oi or 1), 2)
result = {
"symbol": symbol.upper(),
"underlying": records.get('underlyingValue'),
"pcr": pcr,
"sentiment": "Bullish" if pcr > 1.1 else "Bearish" if pcr < 0.8 else "Neutral",
"timestamp": records.get('timestamp'),
"expiries": records.get('expiryDates', [])[:3],
"data": filtered.get('data', [])[:15] # Truncated for speed
}
set_cache(cache_key, result)
return result
except:
return {"error": "Processing Error"}
@app.get("/api/indices")
async def get_indices():
""" Live Market Sentiment Overview """
cache_key = "indices_v4"
cached = get_cache(cache_key, 5)
if cached: return cached
idx_map = {"^NSEI": "NIFTY 50", "^NSEBANK": "BANK NIFTY", "^BSESN": "SENSEX", "^CNXIT": "NIFTY IT"}
results = []
try:
data = yf.download(list(idx_map.keys()), period="1d", interval="5m", progress=False)
for sym, name in idx_map.items():
try:
curr = float(data['Close'][sym].iloc[-1])
prev = float(data['Open'][sym].iloc[0])
change = curr - prev
results.append({
"name": name,
"symbol": sym,
"price": round(curr, 2),
"change": round(change, 2),
"percent": f"{'+' if change >= 0 else ''}{((change/prev)*100):.2f}%"
})
except: continue
except: pass
set_cache(cache_key, results)
return results
@app.get("/api/stocks")
async def get_stocks():
""" Top Trending Stocks """
cache_key = "stocks_v4"
cached = get_cache(cache_key, 10)
if cached: return cached
stock_list = ["RELIANCE.NS", "TCS.NS", "HDFCBANK.NS", "INFY.NS", "ICICIBANK.NS", "SBIN.NS", "AXISBANK.NS"]
results = []
try:
data = yf.download(stock_list, period="1d", interval="5m", progress=False)
for sym in stock_list:
try:
curr = float(data['Close'][sym].iloc[-1])
prev = float(data['Open'][sym].iloc[0])
change = ((curr-prev)/prev)*100
results.append({
"symbol": sym.replace(".NS", ""),
"price": round(curr, 2),
"changePercent": f"{'+' if change >= 0 else ''}{change:.2f}%",
"trend": "Up" if change > 0 else "Down"
})
except: continue
except: pass
set_cache(cache_key, results)
return results
@app.get("/api/search")
async def search(query: str):
""" Ultra-Fast Search """
try:
s = yf.Search(query, max_results=5)
return [{"symbol": q['symbol'].replace(".NS", ""), "name": q['shortname']} for q in s.quotes if ".NS" in q['symbol']]
except: return []
@app.get("/api/technical")
async def technical(symbol: str):
yf_sym = f"{symbol.upper()}.NS" if "^" not in symbol else symbol
df = get_historical_data(yf_sym)
return calculate_technicals(df)
@app.get("/")
def health():
isOpen, msg = get_market_status()
return {
"engine": "Vantage-Ultra-Pro-V4",
"market_status": msg,
"is_open": isOpen,
"cache_objects": len(cache_store),
"server_time": datetime.now().strftime("%H:%M:%S")
}
if __name__ == "__main__":
import uvicorn
# Use 7860 for Hugging Face standard
uvicorn.run(app, host="0.0.0.0", port=7860)