PaperTrade / fii_flow.py
Khanna, Videh Rakesh Rakesh
Initial deployment to Hugging Face Spaces
41cc612
Raw
History Blame Contribute Delete
5.61 kB
"""
FII/DII flow and Nifty PCR fetcher with JSON cache fallback.
Uses NSE public APIs; falls back to yesterday's cached values on failure.
"""
import json
import os
import time
import datetime
import requests
CACHE_FILE = os.path.join(os.path.dirname(__file__), "fii_pcr_cache.json")
# Browser-like headers required by NSE archives
_HEADERS = {
"User-Agent": (
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/124.0.0.0 Safari/537.36"
),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Referer": "https://www.nseindia.com/",
}
_NSE_FII_URL = (
"https://archives.nseindia.com/content/fo/fii_stats_{date}.csv"
)
_NSE_OPTION_CHAIN_URL = (
"https://www.nseindia.com/api/option-chain-indices?symbol=NIFTY"
)
def _load_cache() -> dict:
try:
with open(CACHE_FILE) as f:
return json.load(f)
except Exception:
return {}
def _save_cache(data: dict) -> None:
try:
with open(CACHE_FILE, "w") as f:
json.dump(data, f, indent=2)
except Exception:
pass
def _cache_is_fresh(cache: dict) -> bool:
ts = cache.get("ts", "")
today = datetime.date.today().isoformat()
yesterday = (datetime.date.today() - datetime.timedelta(days=1)).isoformat()
return ts in (today, yesterday)
def _classify_regime(fii_net: float, dii_net: float) -> str:
if fii_net >= 3000:
return "FII_STRONG_BUY"
elif fii_net >= 1000:
return "FII_BUY"
elif fii_net <= -3000:
return "RISK_OFF"
elif fii_net <= -2000 and dii_net >= 1500:
return "FII_SELLING_DII_ABSORBING"
else:
return "NEUTRAL"
def get_fii_dii_flow() -> dict:
"""
Fetch FII/DII net flow from NSE archives CSV.
Returns dict: {fii_net, dii_net, regime, ts}.
Falls back to cached value (up to 1 day old) on any network error.
Units: crore INR (as published by NSE).
"""
cache = _load_cache()
# Try today and yesterday in case today's file isn't published yet
for offset in (0, 1, 2):
date_obj = datetime.date.today() - datetime.timedelta(days=offset)
date_str = date_obj.strftime("%d%m%Y")
url = _NSE_FII_URL.format(date=date_str)
try:
resp = requests.get(url, headers=_HEADERS, timeout=8)
if resp.status_code != 200:
continue
# NSE CSV columns: Type, Buy, Sell, Net (in crore)
lines = [l.strip() for l in resp.text.splitlines() if l.strip()]
fii_net = dii_net = None
for line in lines:
parts = [p.strip().strip('"') for p in line.split(",")]
if len(parts) < 4:
continue
label = parts[0].upper()
try:
net = float(parts[3].replace(",", ""))
except (ValueError, IndexError):
continue
if "FII" in label or "FOREIGN" in label:
fii_net = net
elif "DII" in label or "DOMESTIC" in label:
dii_net = net
if fii_net is None:
continue
dii_net = dii_net or 0.0
regime = _classify_regime(fii_net, dii_net)
result = {
"fii_net": round(fii_net, 1),
"dii_net": round(dii_net, 1),
"regime": regime,
"ts": date_obj.isoformat(),
}
cache.update(result)
_save_cache(cache)
return result
except Exception:
continue
# Fallback: use cache if fresh enough
if _cache_is_fresh(cache) and "fii_net" in cache:
return {k: cache[k] for k in ("fii_net", "dii_net", "regime", "ts")}
# Last resort: neutral defaults
return {"fii_net": 0.0, "dii_net": 0.0, "regime": "NEUTRAL",
"ts": datetime.date.today().isoformat()}
def get_nifty_pcr() -> float | None:
"""
Fetch Nifty Put-Call Ratio from NSE option chain API.
Returns total put OI / total call OI, or None on failure.
Falls back to cached value (up to 1 day old).
"""
cache = _load_cache()
try:
# NSE option chain requires a cookie first
session = requests.Session()
session.get("https://www.nseindia.com", headers=_HEADERS, timeout=8)
time.sleep(0.3)
resp = session.get(_NSE_OPTION_CHAIN_URL, headers=_HEADERS, timeout=10)
if resp.status_code == 200:
data = resp.json()
records = data.get("records", {}).get("data", [])
total_put_oi = sum(
r.get("PE", {}).get("openInterest", 0) for r in records if r.get("PE")
)
total_call_oi = sum(
r.get("CE", {}).get("openInterest", 0) for r in records if r.get("CE")
)
if total_call_oi > 0:
pcr = round(total_put_oi / total_call_oi, 2)
cache["pcr"] = pcr
cache["pcr_ts"] = datetime.date.today().isoformat()
_save_cache(cache)
return pcr
except Exception:
pass
# Fallback
pcr_ts = cache.get("pcr_ts", "")
yesterday = (datetime.date.today() - datetime.timedelta(days=1)).isoformat()
if pcr_ts >= yesterday and "pcr" in cache:
return float(cache["pcr"])
return None
if __name__ == "__main__":
print("FII/DII flow:", get_fii_dii_flow())
print("Nifty PCR:", get_nifty_pcr())