wiz-trader 0.36.0__py3-none-any.whl → 0.38.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
wiz_trader/apis/client.py CHANGED
@@ -226,7 +226,58 @@ class WizzerClient:
226
226
  "kv.delete": "/kv/{strategy_id}/{key}",
227
227
  "kv.list": "/kv/{strategy_id}",
228
228
  "kv.keys": "/kv/{strategy_id}/keys",
229
- "kv.delete_all": "/kv/{strategy_id}/all"
229
+ "kv.delete_all": "/kv/{strategy_id}/all",
230
+
231
+ # Analytics API endpoints - Fundamentals
232
+ "analytics.fundamentals.net_profit_margin": "/analytics/fundamentals/margins/netProfit",
233
+ "analytics.fundamentals.roe": "/analytics/fundamentals/roe",
234
+ "analytics.fundamentals.roa": "/analytics/fundamentals/roa",
235
+ "analytics.fundamentals.ebit_margin": "/analytics/fundamentals/ebit-margin",
236
+ "analytics.fundamentals.ocf_netprofit_ratio": "/analytics/fundamentals/ocf-netprofit-ratio",
237
+ "analytics.fundamentals.eps_cagr": "/analytics/fundamentals/eps-cagr",
238
+ "analytics.fundamentals.book_to_market": "/analytics/fundamentals/valuation/book-to-market",
239
+ "analytics.fundamentals.marketcap_to_sales": "/analytics/fundamentals/valuation/marketcap-to-sales",
240
+ "analytics.fundamentals.cash_to_marketcap": "/analytics/fundamentals/liquidity/cash-to-marketcap",
241
+
242
+ # Analytics API endpoints - Valuation
243
+ "analytics.valuation.pe_ratio": "/analytics/valuation/pe-ratio",
244
+ "analytics.valuation.pb_ratio": "/analytics/valuation/pb-ratio",
245
+ "analytics.valuation.ev_ebitda": "/analytics/valuation/ev-ebitda",
246
+ "analytics.valuation.fcf_yield": "/analytics/valuation/fcf-yield",
247
+
248
+ # Analytics API endpoints - Returns
249
+ "analytics.returns.quarterly": "/analytics/returns/quarterly",
250
+ "analytics.returns.monthly": "/analytics/returns/monthly",
251
+ "analytics.returns.cagr": "/analytics/returns/cagr",
252
+
253
+ # Analytics API endpoints - Market Data
254
+ "analytics.marketdata.ohlcv_daily": "/analytics/marketdata/ohlcv-daily",
255
+ "analytics.marketdata.historical_prices": "/analytics/marketdata/historical-prices",
256
+ "analytics.marketdata.free_float_market_cap": "/analytics/marketdata/free-float-market-cap",
257
+ "analytics.marketdata.index_ohlc_daily": "/analytics/marketdata/index-ohlc-daily",
258
+
259
+ # Analytics API endpoints - Ownership
260
+ "analytics.ownership.fii_dii": "/analytics/ownership/fii-dii",
261
+ "analytics.ownership.fii_change": "/analytics/ownership/fii-change",
262
+ "analytics.ownership.dii_change": "/analytics/ownership/dii-change",
263
+
264
+ # Analytics API endpoints - Metrics
265
+ "analytics.metrics.sortino_ratio": "/analytics/metrics/sortino-ratio",
266
+ "analytics.metrics.upside_capture": "/analytics/metrics/upside-capture",
267
+
268
+ # Analytics API endpoints - Macro
269
+ "analytics.macro.risk_free_rate": "/analytics/macro/rates/risk-free",
270
+
271
+ # Analytics API endpoints - Risk
272
+ "analytics.risk.max_drawdown": "/analytics/risk/maxDrawdown",
273
+ "analytics.risk.returns_volatility": "/analytics/risk/returnsVolatility",
274
+
275
+ # Analytics API endpoints - Metadata
276
+ "analytics.metadata.sector": "/analytics/metadata/sector",
277
+
278
+ # Analytics API endpoints - Leverage
279
+ "analytics.leverage.debt_equity_ratio": "/analytics/leverage/debtEquityRatio",
280
+
230
281
  }
231
282
 
232
283
  def __init__(
@@ -1768,11 +1819,35 @@ class WizzerClient:
1768
1819
  logger.debug("Fetching screener fields.")
1769
1820
  return self._make_request("GET", endpoint)
1770
1821
 
1822
+ def _normalize_params(self, params: Optional[Dict[str, Any]]) -> Optional[Dict[str, str]]:
1823
+ """
1824
+ Normalize parameters for HTTP requests, converting booleans to lowercase strings.
1825
+
1826
+ Args:
1827
+ params (Optional[Dict[str, Any]]): Raw parameters dictionary.
1828
+
1829
+ Returns:
1830
+ Optional[Dict[str, str]]: Normalized parameters with proper string formatting.
1831
+ """
1832
+ if not params:
1833
+ return None
1834
+
1835
+ normalized = {}
1836
+ for key, value in params.items():
1837
+ if isinstance(value, bool):
1838
+ # Convert Python boolean to lowercase string for API compatibility
1839
+ normalized[key] = "true" if value else "false"
1840
+ elif value is not None:
1841
+ # Convert other values to strings
1842
+ normalized[key] = str(value)
1843
+
1844
+ return normalized
1845
+
1771
1846
  def _make_request(
1772
1847
  self,
1773
1848
  method: str,
1774
1849
  endpoint: str,
1775
- params: Optional[Dict[str, str]] = None,
1850
+ params: Optional[Dict[str, Any]] = None,
1776
1851
  json: Optional[Dict[str, Any]] = None,
1777
1852
  headers: Optional[Dict[str, str]] = None
1778
1853
  ) -> Any:
@@ -1782,7 +1857,7 @@ class WizzerClient:
1782
1857
  Args:
1783
1858
  method (str): HTTP method (GET, POST, etc.)
1784
1859
  endpoint (str): API endpoint path.
1785
- params (Optional[Dict[str, str]]): Query parameters for GET requests.
1860
+ params (Optional[Dict[str, Any]]): Query parameters for GET requests.
1786
1861
  json (Optional[Dict[str, Any]]): JSON payload for POST requests.
1787
1862
  headers (Optional[Dict[str, str]]): Custom headers to override the defaults.
1788
1863
 
@@ -1795,13 +1870,16 @@ class WizzerClient:
1795
1870
  url = f"{self.base_url}{endpoint}"
1796
1871
  request_headers = headers if headers else self.headers
1797
1872
 
1873
+ # Normalize parameters to handle booleans correctly
1874
+ normalized_params = self._normalize_params(params)
1875
+
1798
1876
  try:
1799
1877
  logger.debug("%s request to %s", method, url)
1800
1878
  response = requests.request(
1801
1879
  method=method,
1802
1880
  url=url,
1803
1881
  headers=request_headers,
1804
- params=params,
1882
+ params=normalized_params,
1805
1883
  json=json
1806
1884
  )
1807
1885
  response.raise_for_status()
@@ -2234,3 +2312,1195 @@ class WizzerClient:
2234
2312
 
2235
2313
  logger.debug("Deleting all KV pairs for strategy: %s", strategy_info["id"])
2236
2314
  return self._make_request("DELETE", endpoint)
2315
+
2316
+ # ===== ANALYTICS API METHODS =====
2317
+
2318
+ # --- Fundamentals Methods ---
2319
+
2320
+ def get_net_profit_margin(
2321
+ self,
2322
+ symbol: str,
2323
+ period: str = "quarterly",
2324
+ fiscal_year: Optional[str] = None,
2325
+ quarter: Optional[str] = None
2326
+ ) -> Dict[str, Any]:
2327
+ """
2328
+ Get net profit margin for a stock.
2329
+
2330
+ Args:
2331
+ symbol (str): Stock symbol (e.g., "INFY").
2332
+ period (str, optional): "quarterly" or "annual". Defaults to "quarterly".
2333
+ fiscal_year (str, optional): For annual reports (e.g., "2023").
2334
+ quarter (str, optional): For quarterly reports (e.g., "Q1FY24").
2335
+
2336
+ Returns:
2337
+ Dict[str, Any]: Net profit margin data.
2338
+
2339
+ Example Response:
2340
+ {
2341
+ "symbol": "INFY",
2342
+ "period": "quarterly",
2343
+ "quarter": "Q1FY24",
2344
+ "fiscalYear": null,
2345
+ "netProfitMargin": 16.8,
2346
+ "unit": "%"
2347
+ }
2348
+ """
2349
+ params = self._normalize_params({
2350
+ "symbol": symbol,
2351
+ "period": period
2352
+ })
2353
+
2354
+ if fiscal_year:
2355
+ params["fiscalYear"] = fiscal_year
2356
+ if quarter:
2357
+ params["quarter"] = quarter
2358
+
2359
+ logger.debug("Fetching net profit margin for %s", symbol)
2360
+ return self._make_request("GET", self._routes["analytics.fundamentals.net_profit_margin"], params=params)
2361
+
2362
+ def get_roe(
2363
+ self,
2364
+ symbol: str,
2365
+ period: str = "annual",
2366
+ consolidated: bool = True
2367
+ ) -> Dict[str, Any]:
2368
+ """
2369
+ Get Return on Equity (ROE) for a stock.
2370
+
2371
+ Args:
2372
+ symbol (str): Stock symbol.
2373
+ period (str, optional): "quarterly" or "annual". Defaults to "annual".
2374
+ consolidated (bool, optional): Use consolidated financials. Defaults to True.
2375
+
2376
+ Returns:
2377
+ Dict[str, Any]: ROE data.
2378
+
2379
+ Example Response:
2380
+ {
2381
+ "symbol": "TCS",
2382
+ "period": "annual",
2383
+ "roe": 42.5,
2384
+ "unit": "%"
2385
+ }
2386
+ """
2387
+ params = {
2388
+ "symbol": symbol,
2389
+ "period": period,
2390
+ "consolidated": consolidated
2391
+ }
2392
+
2393
+ logger.debug("Fetching ROE for %s", symbol)
2394
+ return self._make_request("GET", self._routes["analytics.fundamentals.roe"], params=params)
2395
+
2396
+ def get_roa(
2397
+ self,
2398
+ symbol: str,
2399
+ period: str = "annual",
2400
+ consolidated: bool = True
2401
+ ) -> Dict[str, Any]:
2402
+ """
2403
+ Get Return on Assets (ROA) for a stock.
2404
+
2405
+ Args:
2406
+ symbol (str): Stock symbol.
2407
+ period (str, optional): "quarterly" or "annual". Defaults to "annual".
2408
+ consolidated (bool, optional): Use consolidated financials. Defaults to True.
2409
+
2410
+ Returns:
2411
+ Dict[str, Any]: ROA data.
2412
+
2413
+ Example Response:
2414
+ {
2415
+ "symbol": "WIPRO",
2416
+ "period": "annual",
2417
+ "roa": 14.3,
2418
+ "unit": "%"
2419
+ }
2420
+ """
2421
+ params = {
2422
+ "symbol": symbol,
2423
+ "period": period,
2424
+ "consolidated": consolidated
2425
+ }
2426
+
2427
+ logger.debug("Fetching ROA for %s", symbol)
2428
+ return self._make_request("GET", self._routes["analytics.fundamentals.roa"], params=params)
2429
+
2430
+ def get_ebit_margin(
2431
+ self,
2432
+ symbol: str,
2433
+ period: str = "annual",
2434
+ consolidated: bool = True
2435
+ ) -> Dict[str, Any]:
2436
+ """
2437
+ Get EBIT margin for a stock.
2438
+
2439
+ Args:
2440
+ symbol (str): Stock symbol.
2441
+ period (str, optional): "quarterly" or "annual". Defaults to "annual".
2442
+ consolidated (bool, optional): Use consolidated financials. Defaults to True.
2443
+
2444
+ Returns:
2445
+ Dict[str, Any]: EBIT margin data.
2446
+
2447
+ Example Response:
2448
+ {
2449
+ "symbol": "INFY",
2450
+ "period": "annual",
2451
+ "ebit_margin": 24.1,
2452
+ "unit": "%"
2453
+ }
2454
+ """
2455
+ params = {
2456
+ "symbol": symbol,
2457
+ "period": period,
2458
+ "consolidated": consolidated
2459
+ }
2460
+
2461
+ logger.debug("Fetching EBIT margin for %s", symbol)
2462
+ return self._make_request("GET", self._routes["analytics.fundamentals.ebit_margin"], params=params)
2463
+
2464
+ def get_ocf_netprofit_ratio(
2465
+ self,
2466
+ symbol: str,
2467
+ period: str = "annual"
2468
+ ) -> Dict[str, Any]:
2469
+ """
2470
+ Get Operating Cash Flow to Net Profit ratio.
2471
+
2472
+ Args:
2473
+ symbol (str): Stock symbol.
2474
+ period (str, optional): "annual" or "ttm". Defaults to "annual".
2475
+
2476
+ Returns:
2477
+ Dict[str, Any]: OCF/Net Profit ratio data.
2478
+
2479
+ Example Response:
2480
+ {
2481
+ "symbol": "TCS",
2482
+ "period": "annual",
2483
+ "ocf_netprofit_ratio": 1.15,
2484
+ "unit": "ratio"
2485
+ }
2486
+
2487
+ Note: Ratio > 1 indicates strong cash generation.
2488
+ """
2489
+ params = {
2490
+ "symbol": symbol,
2491
+ "period": period
2492
+ }
2493
+
2494
+ logger.debug("Fetching OCF/Net Profit ratio for %s", symbol)
2495
+ return self._make_request("GET", self._routes["analytics.fundamentals.ocf_netprofit_ratio"], params=params)
2496
+
2497
+ def get_eps_cagr(
2498
+ self,
2499
+ symbol: str,
2500
+ start_year: int,
2501
+ end_year: int
2502
+ ) -> Dict[str, Any]:
2503
+ """
2504
+ Get EPS Compound Annual Growth Rate (CAGR).
2505
+
2506
+ Args:
2507
+ symbol (str): Stock symbol.
2508
+ start_year (int): Starting year (e.g., 2019).
2509
+ end_year (int): Ending year (e.g., 2023).
2510
+
2511
+ Returns:
2512
+ Dict[str, Any]: EPS CAGR data.
2513
+
2514
+ Example Response:
2515
+ {
2516
+ "symbol": "INFY",
2517
+ "startYear": 2019,
2518
+ "endYear": 2023,
2519
+ "epsCagr": 8.5,
2520
+ "epsStart": 35.2,
2521
+ "epsEnd": 48.7,
2522
+ "years": 4,
2523
+ "unit": "%"
2524
+ }
2525
+ """
2526
+ params = self._normalize_params({
2527
+ "symbol": symbol,
2528
+ "startYear": start_year,
2529
+ "endYear": end_year
2530
+ })
2531
+
2532
+ logger.debug("Fetching EPS CAGR for %s from %s to %s", symbol, start_year, end_year)
2533
+ return self._make_request("GET", self._routes["analytics.fundamentals.eps_cagr"], params=params)
2534
+
2535
+ def get_book_to_market(
2536
+ self,
2537
+ symbol: str,
2538
+ as_of: str,
2539
+ price_source: str = "avg_quarter",
2540
+ custom_price: Optional[float] = None,
2541
+ standalone: bool = False,
2542
+ currency: str = "INR"
2543
+ ) -> Dict[str, Any]:
2544
+ """
2545
+ Get book-to-market ratio for a stock.
2546
+
2547
+ Args:
2548
+ symbol (str): Stock symbol (e.g., "NSE:INFY").
2549
+ as_of (str): Reference date (YYYY-MM-DD).
2550
+ price_source (str, optional): Price source: spot, avg_quarter, custom. Defaults to "avg_quarter".
2551
+ custom_price (float, optional): Required if price_source=custom.
2552
+ standalone (bool, optional): Use standalone financials. Defaults to False.
2553
+ currency (str, optional): Output currency. Defaults to "INR".
2554
+
2555
+ Returns:
2556
+ Dict[str, Any]: Book-to-market ratio data.
2557
+
2558
+ Example Response:
2559
+ {
2560
+ "symbol": "NSE:INFY",
2561
+ "asOf": "2023-03-31",
2562
+ "bookToMarket": 0.1234,
2563
+ "bookValuePerShare": 184.50,
2564
+ "marketPricePerShare": 1495.75,
2565
+ "sourcePriceType": "avg_quarter",
2566
+ "quarterRef": "Q4FY23",
2567
+ "standalone": false,
2568
+ "unit": "ratio"
2569
+ }
2570
+ """
2571
+ params = self._normalize_params({
2572
+ "symbol": symbol,
2573
+ "asOf": as_of,
2574
+ "priceSource": price_source,
2575
+ "standalone": standalone,
2576
+ "currency": currency
2577
+ })
2578
+
2579
+ if custom_price is not None:
2580
+ params["customPrice"] = str(custom_price)
2581
+
2582
+ logger.debug("Fetching book-to-market ratio for %s", symbol)
2583
+ return self._make_request("GET", self._routes["analytics.fundamentals.book_to_market"], params=params)
2584
+
2585
+ def get_marketcap_to_sales(
2586
+ self,
2587
+ symbol: str,
2588
+ as_of: str,
2589
+ price_source: str = "avg_quarter",
2590
+ custom_price: Optional[float] = None,
2591
+ standalone: bool = False
2592
+ ) -> Dict[str, Any]:
2593
+ """
2594
+ Get market cap to sales ratio for a stock.
2595
+
2596
+ Args:
2597
+ symbol (str): Stock symbol.
2598
+ as_of (str): Reference date (YYYY-MM-DD).
2599
+ price_source (str, optional): Price source. Defaults to "avg_quarter".
2600
+ custom_price (float, optional): Custom price if price_source=custom.
2601
+ standalone (bool, optional): Use standalone financials. Defaults to False.
2602
+
2603
+ Returns:
2604
+ Dict[str, Any]: Market cap to sales ratio data.
2605
+
2606
+ Example Response:
2607
+ {
2608
+ "symbol": "INFY",
2609
+ "asOf": "2023-03-31",
2610
+ "marketcapToSales": 5.67,
2611
+ "marketCap": 615000,
2612
+ "sales": 108500,
2613
+ "pricePerShare": 1495.75,
2614
+ "sharesOutstanding": 4112000000,
2615
+ "revenueQuarter": "Q1",
2616
+ "standalone": false,
2617
+ "unit": "ratio"
2618
+ }
2619
+ """
2620
+ params = self._normalize_params({
2621
+ "symbol": symbol,
2622
+ "asOf": as_of,
2623
+ "priceSource": price_source,
2624
+ "standalone": standalone
2625
+ })
2626
+
2627
+ if custom_price is not None:
2628
+ params["customPrice"] = str(custom_price)
2629
+
2630
+ logger.debug("Fetching market cap to sales ratio for %s", symbol)
2631
+ return self._make_request("GET", self._routes["analytics.fundamentals.marketcap_to_sales"], params=params)
2632
+
2633
+ def get_cash_to_marketcap(
2634
+ self,
2635
+ symbol: str,
2636
+ as_of: str,
2637
+ price_source: str = "avg_quarter",
2638
+ custom_price: Optional[float] = None,
2639
+ standalone: bool = False
2640
+ ) -> Dict[str, Any]:
2641
+ """
2642
+ Get cash to market cap ratio for a stock.
2643
+
2644
+ Args:
2645
+ symbol (str): Stock symbol.
2646
+ as_of (str): Reference date (YYYY-MM-DD).
2647
+ price_source (str, optional): Price source. Defaults to "avg_quarter".
2648
+ custom_price (float, optional): Custom price if price_source=custom.
2649
+ standalone (bool, optional): Use standalone financials. Defaults to False.
2650
+
2651
+ Returns:
2652
+ Dict[str, Any]: Cash to market cap ratio data.
2653
+
2654
+ Example Response:
2655
+ {
2656
+ "symbol": "INFY",
2657
+ "asOf": "2023-03-31",
2658
+ "cashToMarketcap": 0.0456,
2659
+ "cashAndEquivalents": 28050,
2660
+ "marketCap": 615000,
2661
+ "pricePerShare": 1495.75,
2662
+ "sharesOutstanding": 4112000000,
2663
+ "reportingQuarter": "Q1",
2664
+ "standalone": false,
2665
+ "unit": "ratio"
2666
+ }
2667
+ """
2668
+ params = self._normalize_params({
2669
+ "symbol": symbol,
2670
+ "asOf": as_of,
2671
+ "priceSource": price_source,
2672
+ "standalone": standalone
2673
+ })
2674
+
2675
+ if custom_price is not None:
2676
+ params["customPrice"] = str(custom_price)
2677
+
2678
+ logger.debug("Fetching cash to market cap ratio for %s", symbol)
2679
+ return self._make_request("GET", self._routes["analytics.fundamentals.cash_to_marketcap"], params=params)
2680
+
2681
+ # --- Valuation Methods ---
2682
+
2683
+ def get_pe_ratio(
2684
+ self,
2685
+ symbol: str,
2686
+ date: Optional[str] = None,
2687
+ ttm: bool = False,
2688
+ consolidated: bool = True,
2689
+ standalone: bool = False
2690
+ ) -> Dict[str, Any]:
2691
+ """
2692
+ Get Price to Earnings (P/E) ratio.
2693
+
2694
+ Args:
2695
+ symbol (str): Stock symbol.
2696
+ date (str, optional): Date in "YYYY-MM-DD" format.
2697
+ ttm (bool, optional): Use trailing twelve months. Defaults to False.
2698
+ consolidated (bool, optional): Use consolidated financials. Defaults to True.
2699
+ standalone (bool, optional): Use standalone financials. Defaults to False.
2700
+
2701
+ Returns:
2702
+ Dict[str, Any]: P/E ratio data.
2703
+
2704
+ Example Response:
2705
+ {
2706
+ "symbol": "RELIANCE",
2707
+ "date": "2025-07-31",
2708
+ "pe_ratio": 27.5,
2709
+ "price": null,
2710
+ "eps": null,
2711
+ "ttm": false,
2712
+ "consolidated": true,
2713
+ "unit": "ratio"
2714
+ }
2715
+ """
2716
+ params = {
2717
+ "symbol": symbol,
2718
+ "ttm": ttm,
2719
+ "consolidated": consolidated,
2720
+ "standalone": standalone
2721
+ }
2722
+
2723
+ if date:
2724
+ params["date"] = date
2725
+
2726
+ logger.debug("Fetching P/E ratio for %s", symbol)
2727
+ return self._make_request("GET", self._routes["analytics.valuation.pe_ratio"], params=params)
2728
+
2729
+ def get_pb_ratio(
2730
+ self,
2731
+ symbol: str,
2732
+ date: Optional[str] = None,
2733
+ consolidated: bool = True,
2734
+ standalone: bool = False
2735
+ ) -> Dict[str, Any]:
2736
+ """
2737
+ Get Price to Book (P/B) ratio.
2738
+
2739
+ Args:
2740
+ symbol (str): Stock symbol.
2741
+ date (str, optional): Date in "YYYY-MM-DD" format.
2742
+ consolidated (bool, optional): Use consolidated financials. Defaults to True.
2743
+ standalone (bool, optional): Use standalone financials. Defaults to False.
2744
+
2745
+ Returns:
2746
+ Dict[str, Any]: P/B ratio data.
2747
+
2748
+ Example Response:
2749
+ {
2750
+ "symbol": "HDFC",
2751
+ "date": "2025-07-31",
2752
+ "pb_ratio": 3.2,
2753
+ "price": null,
2754
+ "book_value": null,
2755
+ "consolidated": true,
2756
+ "unit": "ratio"
2757
+ }
2758
+ """
2759
+ params = {
2760
+ "symbol": symbol,
2761
+ "consolidated": consolidated,
2762
+ "standalone": standalone
2763
+ }
2764
+
2765
+ if date:
2766
+ params["date"] = date
2767
+
2768
+ logger.debug("Fetching P/B ratio for %s", symbol)
2769
+ return self._make_request("GET", self._routes["analytics.valuation.pb_ratio"], params=params)
2770
+
2771
+ def get_ev_ebitda(
2772
+ self,
2773
+ symbol: str,
2774
+ date: Optional[str] = None,
2775
+ ttm: bool = False,
2776
+ consolidated: bool = True,
2777
+ standalone: bool = False
2778
+ ) -> Dict[str, Any]:
2779
+ """
2780
+ Get Enterprise Value to EBITDA ratio.
2781
+
2782
+ Args:
2783
+ symbol (str): Stock symbol.
2784
+ date (str, optional): Date in "YYYY-MM-DD" format.
2785
+ ttm (bool, optional): Use trailing twelve months. Defaults to False.
2786
+ consolidated (bool, optional): Use consolidated financials. Defaults to True.
2787
+ standalone (bool, optional): Use standalone financials. Defaults to False.
2788
+
2789
+ Returns:
2790
+ Dict[str, Any]: EV/EBITDA ratio data.
2791
+
2792
+ Example Response:
2793
+ {
2794
+ "symbol": "LTI",
2795
+ "date": "2025-06-30",
2796
+ "ev_ebitda": 22.8,
2797
+ "enterprise_value": null,
2798
+ "ebitda": null,
2799
+ "ttm": false,
2800
+ "unit": "ratio"
2801
+ }
2802
+ """
2803
+ params = {
2804
+ "symbol": symbol,
2805
+ "ttm": ttm,
2806
+ "consolidated": consolidated,
2807
+ "standalone": standalone
2808
+ }
2809
+
2810
+ if date:
2811
+ params["date"] = date
2812
+
2813
+ logger.debug("Fetching EV/EBITDA for %s", symbol)
2814
+ return self._make_request("GET", self._routes["analytics.valuation.ev_ebitda"], params=params)
2815
+
2816
+ def get_fcf_yield(
2817
+ self,
2818
+ symbol: str,
2819
+ date: Optional[str] = None,
2820
+ ttm: bool = False,
2821
+ consolidated: bool = True,
2822
+ standalone: bool = False
2823
+ ) -> Dict[str, Any]:
2824
+ """
2825
+ Get Free Cash Flow yield.
2826
+
2827
+ Args:
2828
+ symbol (str): Stock symbol.
2829
+ date (str, optional): Date in "YYYY-MM-DD" format.
2830
+ ttm (bool, optional): Use trailing twelve months. Defaults to False.
2831
+ consolidated (bool, optional): Use consolidated financials. Defaults to True.
2832
+ standalone (bool, optional): Use standalone financials. Defaults to False.
2833
+
2834
+ Returns:
2835
+ Dict[str, Any]: FCF yield data.
2836
+
2837
+ Example Response:
2838
+ {
2839
+ "symbol": "HDFCAMC",
2840
+ "date": "2025-06-30",
2841
+ "fcf_yield": 4.5,
2842
+ "fcf": null,
2843
+ "market_cap": null,
2844
+ "ttm": false,
2845
+ "unit": "%"
2846
+ }
2847
+ """
2848
+ params = {
2849
+ "symbol": symbol,
2850
+ "ttm": ttm,
2851
+ "consolidated": consolidated,
2852
+ "standalone": standalone
2853
+ }
2854
+
2855
+ if date:
2856
+ params["date"] = date
2857
+
2858
+ logger.debug("Fetching FCF yield for %s", symbol)
2859
+ return self._make_request("GET", self._routes["analytics.valuation.fcf_yield"], params=params)
2860
+
2861
+ # --- Returns Methods ---
2862
+
2863
+
2864
+ def get_quarterly_returns(
2865
+ self,
2866
+ symbol: str,
2867
+ start_date: str,
2868
+ end_date: str,
2869
+ adjusted: bool = True
2870
+ ) -> Dict[str, Any]:
2871
+ """
2872
+ Get quarterly returns for a stock.
2873
+
2874
+ Args:
2875
+ symbol (str): Stock symbol.
2876
+ start_date (str): Start date in "YYYY-MM-DD" format.
2877
+ end_date (str): End date in "YYYY-MM-DD" format.
2878
+ adjusted (bool, optional): Use adjusted prices. Defaults to True.
2879
+
2880
+ Returns:
2881
+ Dict[str, Any]: Quarterly returns data.
2882
+ """
2883
+ params = self._normalize_params({
2884
+ "symbol": symbol,
2885
+ "startDate": start_date,
2886
+ "endDate": end_date,
2887
+ "adjusted": adjusted
2888
+ })
2889
+
2890
+ logger.debug("Fetching quarterly returns for %s", symbol)
2891
+ return self._make_request("GET", self._routes["analytics.returns.quarterly"], params=params)
2892
+
2893
+ def get_monthly_returns(
2894
+ self,
2895
+ symbol: str,
2896
+ start_date: str,
2897
+ end_date: str,
2898
+ adjusted: bool = True
2899
+ ) -> Dict[str, Any]:
2900
+ """
2901
+ Get monthly returns for a stock.
2902
+
2903
+ Args:
2904
+ symbol (str): Stock symbol.
2905
+ start_date (str): Start date in "YYYY-MM-DD" format.
2906
+ end_date (str): End date in "YYYY-MM-DD" format.
2907
+ adjusted (bool, optional): Use adjusted prices. Defaults to True.
2908
+
2909
+ Returns:
2910
+ Dict[str, Any]: Monthly returns data.
2911
+ """
2912
+ params = self._normalize_params({
2913
+ "symbol": symbol,
2914
+ "startDate": start_date,
2915
+ "endDate": end_date,
2916
+ "adjusted": adjusted
2917
+ })
2918
+
2919
+ logger.debug("Fetching monthly returns for %s", symbol)
2920
+ return self._make_request("GET", self._routes["analytics.returns.monthly"], params=params)
2921
+
2922
+ # --- Market Data Methods ---
2923
+
2924
+ def get_analytics_ohlcv_daily(
2925
+ self,
2926
+ symbol: str,
2927
+ start_date: str,
2928
+ end_date: str,
2929
+ adjusted: bool = True
2930
+ ) -> Dict[str, Any]:
2931
+ """
2932
+ Get daily OHLCV data from analytics API.
2933
+
2934
+ Args:
2935
+ symbol (str): Stock symbol.
2936
+ start_date (str): Start date in "YYYY-MM-DD" format.
2937
+ end_date (str): End date in "YYYY-MM-DD" format.
2938
+ adjusted (bool, optional): Use adjusted prices. Defaults to True (ignored as adjusted data not available).
2939
+
2940
+ Returns:
2941
+ Dict[str, Any]: Daily OHLCV data.
2942
+
2943
+ Example Response:
2944
+ {
2945
+ "data": [
2946
+ {
2947
+ "date": "2025-01-01",
2948
+ "open": 2305.0,
2949
+ "high": 2340.0,
2950
+ "low": 2290.0,
2951
+ "close": 2325.0,
2952
+ "volume": 12500000,
2953
+ "symbol": "RELIANCE"
2954
+ }
2955
+ ]
2956
+ }
2957
+
2958
+ Note: Maximum 365 days per request.
2959
+ """
2960
+ params = self._normalize_params({
2961
+ "symbol": symbol,
2962
+ "startDate": start_date,
2963
+ "endDate": end_date,
2964
+ "adjusted": adjusted
2965
+ })
2966
+
2967
+ logger.debug("Fetching analytics OHLCV daily data for %s", symbol)
2968
+ return self._make_request("GET", self._routes["analytics.marketdata.ohlcv_daily"], params=params)
2969
+
2970
+ def get_analytics_historical_prices(
2971
+ self,
2972
+ symbol: str,
2973
+ start_date: str,
2974
+ end_date: str,
2975
+ adjusted: bool = True
2976
+ ) -> Dict[str, Any]:
2977
+ """
2978
+ Get historical prices from analytics API (same as OHLCV daily).
2979
+
2980
+ Args:
2981
+ symbol (str): Stock symbol.
2982
+ start_date (str): Start date in "YYYY-MM-DD" format.
2983
+ end_date (str): End date in "YYYY-MM-DD" format.
2984
+ adjusted (bool, optional): Use adjusted prices. Defaults to True.
2985
+
2986
+ Returns:
2987
+ Dict[str, Any]: Historical price data.
2988
+ """
2989
+ params = self._normalize_params({
2990
+ "symbol": symbol,
2991
+ "startDate": start_date,
2992
+ "endDate": end_date,
2993
+ "adjusted": adjusted
2994
+ })
2995
+
2996
+ logger.debug("Fetching analytics historical prices for %s", symbol)
2997
+ return self._make_request("GET", self._routes["analytics.marketdata.historical_prices"], params=params)
2998
+
2999
+ def get_free_float_market_cap(
3000
+ self,
3001
+ symbol: str,
3002
+ date: Optional[str] = None
3003
+ ) -> Dict[str, Any]:
3004
+ """
3005
+ Get free-float market capitalization.
3006
+
3007
+ Args:
3008
+ symbol (str): Stock symbol.
3009
+ date (str, optional): Date in "YYYY-MM-DD" format. Defaults to most recent.
3010
+
3011
+ Returns:
3012
+ Dict[str, Any]: Free-float market cap data.
3013
+
3014
+ Example Response:
3015
+ {
3016
+ "symbol": "RELIANCE",
3017
+ "date": "2025-07-31",
3018
+ "free_float_market_cap": 1054321.89,
3019
+ "market_cap": 1234567.89,
3020
+ "promoter_holding_percent": 14.6,
3021
+ "unit": "₹ Crores"
3022
+ }
3023
+ """
3024
+ params = {"symbol": symbol}
3025
+
3026
+ if date:
3027
+ params["date"] = date
3028
+
3029
+ logger.debug("Fetching free-float market cap for %s", symbol)
3030
+ return self._make_request("GET", self._routes["analytics.marketdata.free_float_market_cap"], params=params)
3031
+
3032
+ # --- Ownership Methods ---
3033
+
3034
+ def get_fii_dii_holdings(
3035
+ self,
3036
+ symbol: str,
3037
+ quarter: Optional[str] = None
3038
+ ) -> Dict[str, Any]:
3039
+ """
3040
+ Get FII and DII holdings percentages.
3041
+
3042
+ Args:
3043
+ symbol (str): Stock symbol.
3044
+ quarter (str, optional): Quarter in "Q1FY24" format. Defaults to latest.
3045
+
3046
+ Returns:
3047
+ Dict[str, Any]: FII and DII holdings data.
3048
+
3049
+ Example Response:
3050
+ {
3051
+ "symbol": "RELIANCE",
3052
+ "quarter": "Q4FY23",
3053
+ "fii_percentage": 24.3,
3054
+ "dii_percentage": 18.7,
3055
+ "institutional_total": 43.0,
3056
+ "unit": "%"
3057
+ }
3058
+ """
3059
+ params = {"symbol": symbol}
3060
+
3061
+ if quarter:
3062
+ params["quarter"] = quarter
3063
+
3064
+ logger.debug("Fetching FII/DII holdings for %s", symbol)
3065
+ return self._make_request("GET", self._routes["analytics.ownership.fii_dii"], params=params)
3066
+
3067
+ def get_fii_change(self, symbol: str) -> Dict[str, Any]:
3068
+ """
3069
+ Get FII holding change from previous quarter.
3070
+
3071
+ Args:
3072
+ symbol (str): Stock symbol.
3073
+
3074
+ Returns:
3075
+ Dict[str, Any]: FII change data.
3076
+
3077
+ Example Response:
3078
+ {
3079
+ "symbol": "INFY",
3080
+ "quarter": "2025-03-31",
3081
+ "fii_change": 1.2,
3082
+ "current_fii": 33.5,
3083
+ "previous_fii": 32.3,
3084
+ "unit": "%"
3085
+ }
3086
+ """
3087
+ params = {"symbol": symbol}
3088
+
3089
+ logger.debug("Fetching FII change for %s", symbol)
3090
+ return self._make_request("GET", self._routes["analytics.ownership.fii_change"], params=params)
3091
+
3092
+ def get_dii_change(self, symbol: str) -> Dict[str, Any]:
3093
+ """
3094
+ Get DII holding change from previous quarter.
3095
+
3096
+ Args:
3097
+ symbol (str): Stock symbol.
3098
+
3099
+ Returns:
3100
+ Dict[str, Any]: DII change data.
3101
+
3102
+ Example Response:
3103
+ {
3104
+ "symbol": "INFY",
3105
+ "quarter": "2025-03-31",
3106
+ "dii_change": -0.5,
3107
+ "current_dii": 15.2,
3108
+ "previous_dii": 15.7,
3109
+ "unit": "%"
3110
+ }
3111
+ """
3112
+ params = {"symbol": symbol}
3113
+
3114
+ logger.debug("Fetching DII change for %s", symbol)
3115
+ return self._make_request("GET", self._routes["analytics.ownership.dii_change"], params=params)
3116
+
3117
+ # --- Index Data Methods ---
3118
+
3119
+ def get_index_ohlc_daily(
3120
+ self,
3121
+ symbol: str,
3122
+ start_date: str,
3123
+ end_date: str
3124
+ ) -> Dict[str, Any]:
3125
+ """
3126
+ Get daily OHLC data for an index.
3127
+
3128
+ Args:
3129
+ symbol (str): Index symbol (e.g., "NIFTY50").
3130
+ start_date (str): Start date in "YYYY-MM-DD" format.
3131
+ end_date (str): End date in "YYYY-MM-DD" format.
3132
+
3133
+ Returns:
3134
+ Dict[str, Any]: Index OHLC data.
3135
+
3136
+ Example Response:
3137
+ {
3138
+ "data": [
3139
+ {
3140
+ "date": "2025-01-01",
3141
+ "open": 18250.0,
3142
+ "high": 18350.0,
3143
+ "low": 18200.0,
3144
+ "close": 18325.0,
3145
+ "symbol": "NIFTY50"
3146
+ }
3147
+ ]
3148
+ }
3149
+ """
3150
+ params = self._normalize_params({
3151
+ "symbol": symbol,
3152
+ "startDate": start_date,
3153
+ "endDate": end_date
3154
+ })
3155
+
3156
+ logger.debug("Fetching index OHLC daily data for %s", symbol)
3157
+ return self._make_request("GET", self._routes["analytics.marketdata.index_ohlc_daily"], params=params)
3158
+
3159
+ # --- Metrics Methods ---
3160
+
3161
+ def get_sortino_ratio(
3162
+ self,
3163
+ symbol: str,
3164
+ start_date: str,
3165
+ end_date: str,
3166
+ rf: float = 0.065,
3167
+ interval: str = "daily"
3168
+ ) -> Dict[str, Any]:
3169
+ """
3170
+ Get Sortino ratio for a stock/strategy/index.
3171
+
3172
+ Args:
3173
+ symbol (str): Stock/strategy/index symbol.
3174
+ start_date (str): Start date in "YYYY-MM-DD" format.
3175
+ end_date (str): End date in "YYYY-MM-DD" format.
3176
+ rf (float, optional): Annualized risk-free rate. Defaults to 0.065.
3177
+ interval (str, optional): Return frequency: daily, weekly, monthly. Defaults to "daily".
3178
+
3179
+ Returns:
3180
+ Dict[str, Any]: Sortino ratio data.
3181
+
3182
+ Example Response:
3183
+ {
3184
+ "symbol": "HDFCBANK",
3185
+ "startDate": "2024-01-01",
3186
+ "endDate": "2025-01-01",
3187
+ "interval": "daily",
3188
+ "rf": 0.065,
3189
+ "sortinoRatio": 1.8542,
3190
+ "annualizedReturn": 0.1234,
3191
+ "downsideDeviation": 0.0321,
3192
+ "unit": "ratio"
3193
+ }
3194
+ """
3195
+ params = self._normalize_params({
3196
+ "symbol": symbol,
3197
+ "startDate": start_date,
3198
+ "endDate": end_date,
3199
+ "rf": rf,
3200
+ "interval": interval
3201
+ })
3202
+
3203
+ logger.debug("Fetching Sortino ratio for %s", symbol)
3204
+ return self._make_request("GET", self._routes["analytics.metrics.sortino_ratio"], params=params)
3205
+
3206
+ def get_upside_capture(
3207
+ self,
3208
+ symbol: str,
3209
+ benchmark_symbol: str,
3210
+ start_date: str,
3211
+ end_date: str,
3212
+ interval: str = "daily"
3213
+ ) -> Dict[str, Any]:
3214
+ """
3215
+ Get upside capture ratio for a stock/portfolio.
3216
+
3217
+ Args:
3218
+ symbol (str): Stock/portfolio symbol.
3219
+ benchmark_symbol (str): Benchmark symbol.
3220
+ start_date (str): Start date in "YYYY-MM-DD" format.
3221
+ end_date (str): End date in "YYYY-MM-DD" format.
3222
+ interval (str, optional): Comparison interval. Defaults to "daily".
3223
+
3224
+ Returns:
3225
+ Dict[str, Any]: Upside capture ratio data.
3226
+
3227
+ Example Response:
3228
+ {
3229
+ "symbol": "ICICIBANK",
3230
+ "benchmarkSymbol": "NIFTY50",
3231
+ "startDate": "2024-01-01",
3232
+ "endDate": "2025-01-01",
3233
+ "interval": "daily",
3234
+ "upsideCaptureRatio": 112.50,
3235
+ "periodsAnalyzed": 250,
3236
+ "positiveBenchmarkPeriods": 135,
3237
+ "unit": "%"
3238
+ }
3239
+ """
3240
+ params = self._normalize_params({
3241
+ "symbol": symbol,
3242
+ "benchmarkSymbol": benchmark_symbol,
3243
+ "startDate": start_date,
3244
+ "endDate": end_date,
3245
+ "interval": interval
3246
+ })
3247
+
3248
+ logger.debug("Fetching upside capture ratio for %s vs %s", symbol, benchmark_symbol)
3249
+ return self._make_request("GET", self._routes["analytics.metrics.upside_capture"], params=params)
3250
+
3251
+ # --- Macro Methods ---
3252
+
3253
+ def get_risk_free_rate(
3254
+ self,
3255
+ start_date: str,
3256
+ end_date: str,
3257
+ tenor: str = "10Y",
3258
+ country: str = "IN",
3259
+ method: str = "average"
3260
+ ) -> Dict[str, Any]:
3261
+ """
3262
+ Get risk-free rates for various tenors and countries.
3263
+
3264
+ Args:
3265
+ start_date (str): Start date in "YYYY-MM-DD" format.
3266
+ end_date (str): End date in "YYYY-MM-DD" format.
3267
+ tenor (str, optional): Maturity: 3M, 6M, 1Y, 5Y, 10Y. Defaults to "10Y".
3268
+ country (str, optional): Country code. Defaults to "IN".
3269
+ method (str, optional): Calculation: average, start, end, daily_series. Defaults to "average".
3270
+
3271
+ Returns:
3272
+ Dict[str, Any]: Risk-free rate data.
3273
+
3274
+ Example Response:
3275
+ {
3276
+ "country": "IN",
3277
+ "tenor": "10Y",
3278
+ "startDate": "2024-01-01",
3279
+ "endDate": "2024-12-31",
3280
+ "method": "average",
3281
+ "riskFreeRate": 0.0735,
3282
+ "source": "RBI/FIMMDA (Default)",
3283
+ "unit": "decimal"
3284
+ }
3285
+ """
3286
+ params = self._normalize_params({
3287
+ "startDate": start_date,
3288
+ "endDate": end_date,
3289
+ "tenor": tenor,
3290
+ "country": country,
3291
+ "method": method
3292
+ })
3293
+
3294
+ logger.debug("Fetching risk-free rate for %s %s", country, tenor)
3295
+ return self._make_request("GET", self._routes["analytics.macro.risk_free_rate"], params=params)
3296
+
3297
+ # --- Risk Analysis Methods ---
3298
+
3299
+ def get_max_drawdown(
3300
+ self,
3301
+ symbol: str,
3302
+ start_date: str,
3303
+ end_date: str,
3304
+ adjusted: bool = True,
3305
+ interval: str = "daily"
3306
+ ) -> Dict[str, Any]:
3307
+ """
3308
+ Calculate maximum drawdown for a stock over a specified period.
3309
+
3310
+ Args:
3311
+ symbol (str): Stock symbol.
3312
+ start_date (str): Analysis start date (YYYY-MM-DD).
3313
+ end_date (str): Analysis end date (YYYY-MM-DD).
3314
+ adjusted (bool, optional): Use adjusted prices. Defaults to True.
3315
+ interval (str, optional): Data frequency: daily, weekly, monthly. Defaults to "daily".
3316
+
3317
+ Returns:
3318
+ Dict[str, Any]: Maximum drawdown data.
3319
+
3320
+ Example Response:
3321
+ {
3322
+ "symbol": "TCS",
3323
+ "startDate": "2024-01-01",
3324
+ "endDate": "2024-12-31",
3325
+ "adjusted": true,
3326
+ "interval": "daily",
3327
+ "maxDrawdown": -0.1847,
3328
+ "peakDate": "2024-03-15",
3329
+ "troughDate": "2024-06-08",
3330
+ "peakPrice": 4250.50,
3331
+ "troughPrice": 3465.75,
3332
+ "unit": "decimal"
3333
+ }
3334
+ """
3335
+ params = self._normalize_params({
3336
+ "symbol": symbol,
3337
+ "startDate": start_date,
3338
+ "endDate": end_date,
3339
+ "adjusted": adjusted,
3340
+ "interval": interval
3341
+ })
3342
+
3343
+ logger.debug("Fetching max drawdown for %s", symbol)
3344
+ return self._make_request("GET", self._routes["analytics.risk.max_drawdown"], params=params)
3345
+
3346
+ def get_returns_volatility(
3347
+ self,
3348
+ symbol: str,
3349
+ start_date: str,
3350
+ end_date: str,
3351
+ frequency: str = "daily",
3352
+ periods: Optional[int] = None
3353
+ ) -> Dict[str, Any]:
3354
+ """
3355
+ Calculate returns volatility using standard deviation over a specified period.
3356
+
3357
+ Args:
3358
+ symbol (str): Stock symbol.
3359
+ start_date (str): Analysis start date (YYYY-MM-DD).
3360
+ end_date (str): Analysis end date (YYYY-MM-DD).
3361
+ frequency (str, optional): Return frequency: daily, weekly, monthly. Defaults to "daily".
3362
+ periods (int, optional): Rolling window size (5-250). Auto-calculated if not provided.
3363
+
3364
+ Returns:
3365
+ Dict[str, Any]: Volatility analysis data.
3366
+
3367
+ Example Response:
3368
+ {
3369
+ "symbol": "INFY",
3370
+ "startDate": "2024-01-01",
3371
+ "endDate": "2024-06-30",
3372
+ "frequency": "daily",
3373
+ "volatility": 0.0243,
3374
+ "annualizedVolatility": 0.3856,
3375
+ "periods": 125,
3376
+ "unit": "decimal"
3377
+ }
3378
+ """
3379
+ params = self._normalize_params({
3380
+ "symbol": symbol,
3381
+ "startDate": start_date,
3382
+ "endDate": end_date,
3383
+ "frequency": frequency
3384
+ })
3385
+
3386
+ if periods is not None:
3387
+ params["periods"] = str(periods)
3388
+
3389
+ logger.debug("Fetching returns volatility for %s", symbol)
3390
+ return self._make_request("GET", self._routes["analytics.risk.returns_volatility"], params=params)
3391
+
3392
+ # --- Metadata Methods ---
3393
+
3394
+ def get_sector_classification(
3395
+ self,
3396
+ symbol: str
3397
+ ) -> Dict[str, Any]:
3398
+ """
3399
+ Get comprehensive sector and industry classification for a stock.
3400
+
3401
+ Args:
3402
+ symbol (str): Stock symbol.
3403
+
3404
+ Returns:
3405
+ Dict[str, Any]: Sector classification data.
3406
+
3407
+ Example Response:
3408
+ {
3409
+ "symbol": "TATASTEEL",
3410
+ "sector": "Basic Materials",
3411
+ "industry": "Steel",
3412
+ "subIndustry": "Integrated Steel",
3413
+ "classification": "Industrial Metals & Mining",
3414
+ "lastUpdated": "2024-01-15"
3415
+ }
3416
+ """
3417
+ params = self._normalize_params({
3418
+ "symbol": symbol
3419
+ })
3420
+
3421
+ logger.debug("Fetching sector classification for %s", symbol)
3422
+ return self._make_request("GET", self._routes["analytics.metadata.sector"], params=params)
3423
+
3424
+ # --- Leverage Analysis Methods ---
3425
+
3426
+ def get_debt_equity_ratio(
3427
+ self,
3428
+ symbol: str,
3429
+ date: Optional[str] = None,
3430
+ consolidated: bool = True
3431
+ ) -> Dict[str, Any]:
3432
+ """
3433
+ Calculate debt-to-equity ratio using most recent financial data.
3434
+
3435
+ Args:
3436
+ symbol (str): Stock symbol.
3437
+ date (str, optional): Specific quarter-end date (YYYY-MM-DD). Latest if not provided.
3438
+ consolidated (bool, optional): Use consolidated financials. Defaults to True.
3439
+
3440
+ Returns:
3441
+ Dict[str, Any]: Debt-to-equity ratio data.
3442
+
3443
+ Example Response:
3444
+ {
3445
+ "symbol": "RELIANCE",
3446
+ "date": "2024-09-30",
3447
+ "deRatio": 0.3247,
3448
+ "totalDebt": 287450.25,
3449
+ "shareholderEquity": 884972.10,
3450
+ "source": "consolidated",
3451
+ "quarter": "Q2FY25",
3452
+ "unit": "ratio"
3453
+ }
3454
+ """
3455
+ params = self._normalize_params({
3456
+ "symbol": symbol,
3457
+ "consolidated": consolidated
3458
+ })
3459
+
3460
+ if date:
3461
+ params["date"] = date
3462
+
3463
+ logger.debug("Fetching debt-to-equity ratio for %s", symbol)
3464
+ return self._make_request("GET", self._routes["analytics.leverage.debt_equity_ratio"], params=params)
3465
+
3466
+ def get_cagr(
3467
+ self,
3468
+ symbol: str,
3469
+ start_date: str,
3470
+ end_date: str,
3471
+ adjusted: bool = True
3472
+ ) -> Dict[str, Any]:
3473
+ """
3474
+ Calculate Compound Annual Growth Rate (CAGR) over a specified time period.
3475
+
3476
+ Args:
3477
+ symbol (str): Stock symbol.
3478
+ start_date (str): Investment start date (YYYY-MM-DD).
3479
+ end_date (str): Investment end date (YYYY-MM-DD).
3480
+ adjusted (bool, optional): Use adjusted prices. Defaults to True.
3481
+
3482
+ Returns:
3483
+ Dict[str, Any]: CAGR calculation data.
3484
+
3485
+ Example Response:
3486
+ {
3487
+ "symbol": "TCS",
3488
+ "startDate": "2020-01-01",
3489
+ "endDate": "2024-12-31",
3490
+ "cagr": 12.45,
3491
+ "startPrice": 2150.30,
3492
+ "endPrice": 3847.60,
3493
+ "years": 5.0,
3494
+ "adjusted": true,
3495
+ "unit": "%"
3496
+ }
3497
+ """
3498
+ params = self._normalize_params({
3499
+ "symbol": symbol,
3500
+ "startDate": start_date,
3501
+ "endDate": end_date,
3502
+ "adjusted": adjusted
3503
+ })
3504
+
3505
+ logger.debug("Fetching CAGR for %s from %s to %s", symbol, start_date, end_date)
3506
+ return self._make_request("GET", self._routes["analytics.returns.cagr"], params=params)