Jitendra12421 commited on
Commit
cb8ac4b
·
verified ·
1 Parent(s): b770025

Upload 42 files

Browse files
__pycache__/kotak_neo.cpython-311.pyc CHANGED
Binary files a/__pycache__/kotak_neo.cpython-311.pyc and b/__pycache__/kotak_neo.cpython-311.pyc differ
 
kotak_neo.py CHANGED
@@ -4,14 +4,16 @@ import os
4
  import threading
5
  from csv import DictReader
6
  from concurrent.futures import ThreadPoolExecutor, as_completed
7
- from datetime import datetime, timezone
8
  from pathlib import Path
9
  from time import monotonic
10
  from typing import Any
11
  from urllib.parse import quote
 
12
 
13
  import json
14
 
 
15
  import requests
16
 
17
 
@@ -23,6 +25,9 @@ DEFAULT_TIMEOUT_SECONDS = 20
23
  ACCOUNT_TIMEOUT_SECONDS = 7
24
  DATA_DIR = Path(__file__).resolve().parent / "data"
25
  KOTAK_ACTIVITY_LOG_PATH = DATA_DIR / "kotak_activity_log.txt"
 
 
 
26
 
27
 
28
  class KotakNeoError(Exception):
@@ -71,6 +76,15 @@ def _first_market_number(*values: Any) -> float | None:
71
  return fallback
72
 
73
 
 
 
 
 
 
 
 
 
 
74
  def _first_text(*values: Any) -> str | None:
75
  for value in values:
76
  if isinstance(value, dict):
@@ -396,7 +410,7 @@ class KotakNeoManager:
396
  with self._lock:
397
  if not force_refresh:
398
  cached = self._quote_cache.get(cache_key)
399
- if cached and (monotonic() - float(cached.get("stored_at_monotonic") or 0.0)) < 1.0:
400
  return dict(cached["payload"])
401
  context = self._context_locked()
402
 
@@ -416,12 +430,18 @@ class KotakNeoManager:
416
  raise KotakNeoError("Kotak Neo did not return a NIFTY 50 quote.")
417
 
418
  item = items[0]
 
419
  last_traded_price = _first_market_number(item.get("last_traded_price"), item.get("ltp"), item.get("iv"))
420
- close = _first_market_number(item.get("ic"), item.get("close"), item.get("c"))
421
- change = _first_number(item.get("cng"), item.get("change"))
422
- change_pct = _first_number(item.get("nc"), item.get("net_change_percentage"))
423
- high = _first_market_number(item.get("highPrice"), item.get("high"), item.get("h"), item.get("hp"))
424
- low = _first_market_number(item.get("lowPrice"), item.get("low"), item.get("l"), item.get("lp"))
 
 
 
 
 
425
  open_price = _first_market_number(item.get("openingPrice"), item.get("open"), item.get("o"))
426
 
427
  payload = {
@@ -442,6 +462,8 @@ class KotakNeoManager:
442
  "open": open_price,
443
  "high": high,
444
  "low": low,
 
 
445
  "exchange_feed_time": _first_text(item.get("tvalue"), item.get("updRecvTm"), item.get("hsUpTm")),
446
  "as_of": _utc_now_iso(),
447
  "source": {
@@ -450,6 +472,7 @@ class KotakNeoManager:
450
  "instrument_lookup": reference.get("lookup_mode"),
451
  "master_symbol_name": reference.get("master_symbol_name"),
452
  "master_trading_symbol": reference.get("master_trading_symbol"),
 
453
  },
454
  }
455
 
@@ -460,6 +483,60 @@ class KotakNeoManager:
460
  }
461
  return payload
462
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
463
  def _ensure_authenticated_locked(self) -> None:
464
  if not self.edit_token or not self.edit_sid or not self.base_url:
465
  raise KotakNeoSessionRequired("Kotak Neo session is not authenticated.")
 
4
  import threading
5
  from csv import DictReader
6
  from concurrent.futures import ThreadPoolExecutor, as_completed
7
+ from datetime import date, datetime, time, timezone
8
  from pathlib import Path
9
  from time import monotonic
10
  from typing import Any
11
  from urllib.parse import quote
12
+ from zoneinfo import ZoneInfo
13
 
14
  import json
15
 
16
+ import pandas as pd
17
  import requests
18
 
19
 
 
25
  ACCOUNT_TIMEOUT_SECONDS = 7
26
  DATA_DIR = Path(__file__).resolve().parent / "data"
27
  KOTAK_ACTIVITY_LOG_PATH = DATA_DIR / "kotak_activity_log.txt"
28
+ NIFTY_1M_PATH = DATA_DIR / "nifty50_1m.parquet"
29
+ NIFTY_1D_PATH = DATA_DIR / "nifty50_1d.parquet"
30
+ IST = ZoneInfo("Asia/Kolkata")
31
 
32
 
33
  class KotakNeoError(Exception):
 
76
  return fallback
77
 
78
 
79
+ def _normalize_frame_dates(frame: pd.DataFrame) -> pd.Series:
80
+ values = pd.to_datetime(frame["date"], errors="coerce")
81
+ if getattr(values.dt, "tz", None) is None:
82
+ values = values.dt.tz_localize(IST)
83
+ else:
84
+ values = values.dt.tz_convert(IST)
85
+ return values
86
+
87
+
88
  def _first_text(*values: Any) -> str | None:
89
  for value in values:
90
  if isinstance(value, dict):
 
410
  with self._lock:
411
  if not force_refresh:
412
  cached = self._quote_cache.get(cache_key)
413
+ if cached and (monotonic() - float(cached.get("stored_at_monotonic") or 0.0)) < 0.25:
414
  return dict(cached["payload"])
415
  context = self._context_locked()
416
 
 
430
  raise KotakNeoError("Kotak Neo did not return a NIFTY 50 quote.")
431
 
432
  item = items[0]
433
+ now_ist = datetime.now(IST)
434
  last_traded_price = _first_market_number(item.get("last_traded_price"), item.get("ltp"), item.get("iv"))
435
+ live_stats = self._load_nifty50_reference_stats(now_ist, last_traded_price)
436
+ close = live_stats["previous_close"]
437
+ change_base = live_stats["return_base"]
438
+ change = None
439
+ change_pct = None
440
+ if last_traded_price is not None and change_base not in (None, 0):
441
+ change = last_traded_price - change_base
442
+ change_pct = (change / change_base) * 100.0
443
+ high = live_stats["range_high"]
444
+ low = live_stats["range_low"]
445
  open_price = _first_market_number(item.get("openingPrice"), item.get("open"), item.get("o"))
446
 
447
  payload = {
 
462
  "open": open_price,
463
  "high": high,
464
  "low": low,
465
+ "return_basis": live_stats["return_basis"],
466
+ "market_open": live_stats["market_open"],
467
  "exchange_feed_time": _first_text(item.get("tvalue"), item.get("updRecvTm"), item.get("hsUpTm")),
468
  "as_of": _utc_now_iso(),
469
  "source": {
 
472
  "instrument_lookup": reference.get("lookup_mode"),
473
  "master_symbol_name": reference.get("master_symbol_name"),
474
  "master_trading_symbol": reference.get("master_trading_symbol"),
475
+ "reference_data": "backend/data/nifty50_1m.parquet + backend/data/nifty50_1d.parquet",
476
  },
477
  }
478
 
 
483
  }
484
  return payload
485
 
486
+ def _load_nifty50_reference_stats(self, now_ist: datetime, last_traded_price: float | None) -> dict[str, Any]:
487
+ today = now_ist.date()
488
+ market_open = time(9, 15) <= now_ist.time() < time(15, 30)
489
+
490
+ daily = pd.read_parquet(NIFTY_1D_PATH, columns=["date", "open", "high", "low", "close"]).copy()
491
+ daily["date"] = pd.to_datetime(daily["date"], errors="coerce").dt.date
492
+ daily = daily.dropna(subset=["date"]).sort_values("date")
493
+
494
+ previous_close = None
495
+ today_daily = daily[daily["date"] == today]
496
+ previous_daily = daily[daily["date"] < today]
497
+ if not previous_daily.empty:
498
+ previous_close = _to_float(previous_daily.iloc[-1]["close"])
499
+
500
+ today_open = _to_float(today_daily.iloc[-1]["open"]) if not today_daily.empty else None
501
+ today_high = _to_float(today_daily.iloc[-1]["high"]) if not today_daily.empty else None
502
+ today_low = _to_float(today_daily.iloc[-1]["low"]) if not today_daily.empty else None
503
+ today_close = _to_float(today_daily.iloc[-1]["close"]) if not today_daily.empty else None
504
+
505
+ minute = pd.read_parquet(NIFTY_1M_PATH, columns=["date", "open", "high", "low", "close"]).copy()
506
+ minute["date"] = _normalize_frame_dates(minute)
507
+ minute = minute.dropna(subset=["date"]).sort_values("date")
508
+ today_minute = minute[minute["date"].dt.date == today]
509
+
510
+ if not today_minute.empty:
511
+ today_open = _to_float(today_minute.iloc[0]["open"]) or today_open
512
+ minute_high = pd.to_numeric(today_minute["high"], errors="coerce").max()
513
+ minute_low = pd.to_numeric(today_minute["low"], errors="coerce").min()
514
+ today_high = _to_float(minute_high) or today_high
515
+ today_low = _to_float(minute_low) or today_low
516
+ today_close = _to_float(today_minute.iloc[-1]["close"]) or today_close
517
+
518
+ if market_open:
519
+ range_high = max([value for value in [today_high, last_traded_price] if value is not None], default=None)
520
+ range_low = min([value for value in [today_low, last_traded_price] if value is not None], default=None)
521
+ return_base = today_open
522
+ return_basis = "open"
523
+ else:
524
+ range_high = today_high
525
+ range_low = today_low
526
+ return_base = previous_close
527
+ return_basis = "previous_close"
528
+ if last_traded_price is None:
529
+ last_traded_price = today_close
530
+
531
+ return {
532
+ "previous_close": previous_close,
533
+ "return_base": return_base,
534
+ "return_basis": return_basis,
535
+ "range_high": range_high,
536
+ "range_low": range_low,
537
+ "market_open": market_open,
538
+ }
539
+
540
  def _ensure_authenticated_locked(self) -> None:
541
  if not self.edit_token or not self.edit_sid or not self.base_url:
542
  raise KotakNeoSessionRequired("Kotak Neo session is not authenticated.")