wiz-trader 0.37.0__py3-none-any.whl → 0.39.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/__init__.py +1 -1
- wiz_trader/apis/client.py +1645 -4
- {wiz_trader-0.37.0.dist-info → wiz_trader-0.39.0.dist-info}/METADATA +572 -19
- wiz_trader-0.39.0.dist-info/RECORD +9 -0
- wiz_trader-0.37.0.dist-info/RECORD +0 -9
- {wiz_trader-0.37.0.dist-info → wiz_trader-0.39.0.dist-info}/WHEEL +0 -0
- {wiz_trader-0.37.0.dist-info → wiz_trader-0.39.0.dist-info}/top_level.txt +0 -0
wiz_trader/apis/client.py
CHANGED
@@ -226,7 +226,68 @@ 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
|
+
|
281
|
+
# Analytics API endpoints - New Additions
|
282
|
+
"analytics.marketdata.average_volume": "/analytics/marketdata/averageVolume",
|
283
|
+
"analytics.index.max_drawdown": "/analytics/index/metrics/maxDrawdown",
|
284
|
+
"analytics.instrument.drawdown_duration": "/analytics/instrument/metrics/drawdownDuration",
|
285
|
+
"analytics.price.rolling_peak": "/analytics/analytics/price/rollingPeak",
|
286
|
+
"analytics.price.rolling_mean": "/analytics/analytics/price/rollingMean",
|
287
|
+
"analytics.volatility.realized": "/analytics/analytics/volatility/realized",
|
288
|
+
"analytics.risk.beta_90d": "/analytics/risk/beta90d",
|
289
|
+
"analytics.risk.beta_custom": "/analytics/risk/beta",
|
290
|
+
|
230
291
|
}
|
231
292
|
|
232
293
|
def __init__(
|
@@ -1768,11 +1829,37 @@ class WizzerClient:
|
|
1768
1829
|
logger.debug("Fetching screener fields.")
|
1769
1830
|
return self._make_request("GET", endpoint)
|
1770
1831
|
|
1832
|
+
def _normalize_params(self, params: Optional[Dict[str, Any]]) -> Optional[Dict[str, str]]:
|
1833
|
+
"""
|
1834
|
+
Normalize parameters for HTTP requests, converting booleans to lowercase strings.
|
1835
|
+
Preserves spaces in string values to prevent automatic URL encoding by requests library.
|
1836
|
+
|
1837
|
+
Args:
|
1838
|
+
params (Optional[Dict[str, Any]]): Raw parameters dictionary.
|
1839
|
+
|
1840
|
+
Returns:
|
1841
|
+
Optional[Dict[str, str]]: Normalized parameters with proper string formatting.
|
1842
|
+
"""
|
1843
|
+
if not params:
|
1844
|
+
return None
|
1845
|
+
|
1846
|
+
normalized = {}
|
1847
|
+
for key, value in params.items():
|
1848
|
+
if isinstance(value, bool):
|
1849
|
+
# Convert Python boolean to lowercase string for API compatibility
|
1850
|
+
normalized[key] = "true" if value else "false"
|
1851
|
+
elif value is not None:
|
1852
|
+
# Convert other values to strings, preserving spaces
|
1853
|
+
# This prevents requests library from automatically URL-encoding spaces to '+'
|
1854
|
+
normalized[key] = str(value)
|
1855
|
+
|
1856
|
+
return normalized
|
1857
|
+
|
1771
1858
|
def _make_request(
|
1772
1859
|
self,
|
1773
1860
|
method: str,
|
1774
1861
|
endpoint: str,
|
1775
|
-
params: Optional[Dict[str,
|
1862
|
+
params: Optional[Dict[str, Any]] = None,
|
1776
1863
|
json: Optional[Dict[str, Any]] = None,
|
1777
1864
|
headers: Optional[Dict[str, str]] = None
|
1778
1865
|
) -> Any:
|
@@ -1782,7 +1869,7 @@ class WizzerClient:
|
|
1782
1869
|
Args:
|
1783
1870
|
method (str): HTTP method (GET, POST, etc.)
|
1784
1871
|
endpoint (str): API endpoint path.
|
1785
|
-
params (Optional[Dict[str,
|
1872
|
+
params (Optional[Dict[str, Any]]): Query parameters for GET requests.
|
1786
1873
|
json (Optional[Dict[str, Any]]): JSON payload for POST requests.
|
1787
1874
|
headers (Optional[Dict[str, str]]): Custom headers to override the defaults.
|
1788
1875
|
|
@@ -1792,16 +1879,35 @@ class WizzerClient:
|
|
1792
1879
|
Raises:
|
1793
1880
|
requests.RequestException: If the request fails.
|
1794
1881
|
"""
|
1882
|
+
import urllib.parse
|
1883
|
+
|
1795
1884
|
url = f"{self.base_url}{endpoint}"
|
1796
1885
|
request_headers = headers if headers else self.headers
|
1797
1886
|
|
1887
|
+
# Normalize parameters to handle booleans correctly
|
1888
|
+
normalized_params = self._normalize_params(params)
|
1889
|
+
|
1890
|
+
# Handle URL construction manually to encode spaces as %20 instead of +
|
1891
|
+
if normalized_params and method.upper() == 'GET':
|
1892
|
+
# Construct query string manually to control encoding
|
1893
|
+
query_parts = []
|
1894
|
+
for key, value in normalized_params.items():
|
1895
|
+
# Use urllib.parse.quote to encode values, spaces become %20 instead of +
|
1896
|
+
encoded_key = urllib.parse.quote(str(key), safe='')
|
1897
|
+
encoded_value = urllib.parse.quote(str(value), safe='') # Properly encode all special chars
|
1898
|
+
query_parts.append(f"{encoded_key}={encoded_value}")
|
1899
|
+
|
1900
|
+
if query_parts:
|
1901
|
+
url = f"{url}?{'&'.join(query_parts)}"
|
1902
|
+
normalized_params = None # Don't pass params to requests since we built the URL manually
|
1903
|
+
|
1798
1904
|
try:
|
1799
1905
|
logger.debug("%s request to %s", method, url)
|
1800
1906
|
response = requests.request(
|
1801
1907
|
method=method,
|
1802
1908
|
url=url,
|
1803
1909
|
headers=request_headers,
|
1804
|
-
params=
|
1910
|
+
params=normalized_params,
|
1805
1911
|
json=json
|
1806
1912
|
)
|
1807
1913
|
response.raise_for_status()
|
@@ -2234,3 +2340,1538 @@ class WizzerClient:
|
|
2234
2340
|
|
2235
2341
|
logger.debug("Deleting all KV pairs for strategy: %s", strategy_info["id"])
|
2236
2342
|
return self._make_request("DELETE", endpoint)
|
2343
|
+
|
2344
|
+
# ===== ANALYTICS API METHODS =====
|
2345
|
+
|
2346
|
+
# --- Fundamentals Methods ---
|
2347
|
+
|
2348
|
+
def get_net_profit_margin(
|
2349
|
+
self,
|
2350
|
+
symbol: str,
|
2351
|
+
period: str = "quarterly",
|
2352
|
+
fiscal_year: Optional[str] = None,
|
2353
|
+
quarter: Optional[str] = None
|
2354
|
+
) -> Dict[str, Any]:
|
2355
|
+
"""
|
2356
|
+
Get net profit margin for a stock.
|
2357
|
+
|
2358
|
+
Args:
|
2359
|
+
symbol (str): Stock symbol (e.g., "INFY").
|
2360
|
+
period (str, optional): "quarterly" or "annual". Defaults to "quarterly".
|
2361
|
+
fiscal_year (str, optional): For annual reports (e.g., "2023").
|
2362
|
+
quarter (str, optional): For quarterly reports (e.g., "Q1FY24").
|
2363
|
+
|
2364
|
+
Returns:
|
2365
|
+
Dict[str, Any]: Net profit margin data.
|
2366
|
+
|
2367
|
+
Example Response:
|
2368
|
+
{
|
2369
|
+
"symbol": "INFY",
|
2370
|
+
"period": "quarterly",
|
2371
|
+
"quarter": "Q1FY24",
|
2372
|
+
"fiscalYear": null,
|
2373
|
+
"netProfitMargin": 16.8,
|
2374
|
+
"unit": "%"
|
2375
|
+
}
|
2376
|
+
"""
|
2377
|
+
params = self._normalize_params({
|
2378
|
+
"symbol": symbol,
|
2379
|
+
"period": period
|
2380
|
+
})
|
2381
|
+
|
2382
|
+
if fiscal_year:
|
2383
|
+
params["fiscalYear"] = fiscal_year
|
2384
|
+
if quarter:
|
2385
|
+
params["quarter"] = quarter
|
2386
|
+
|
2387
|
+
logger.debug("Fetching net profit margin for %s", symbol)
|
2388
|
+
return self._make_request("GET", self._routes["analytics.fundamentals.net_profit_margin"], params=params)
|
2389
|
+
|
2390
|
+
def get_roe(
|
2391
|
+
self,
|
2392
|
+
symbol: str,
|
2393
|
+
period: str = "annual",
|
2394
|
+
consolidated: bool = True
|
2395
|
+
) -> Dict[str, Any]:
|
2396
|
+
"""
|
2397
|
+
Get Return on Equity (ROE) for a stock.
|
2398
|
+
|
2399
|
+
Args:
|
2400
|
+
symbol (str): Stock symbol.
|
2401
|
+
period (str, optional): "quarterly" or "annual". Defaults to "annual".
|
2402
|
+
consolidated (bool, optional): Use consolidated financials. Defaults to True.
|
2403
|
+
|
2404
|
+
Returns:
|
2405
|
+
Dict[str, Any]: ROE data.
|
2406
|
+
|
2407
|
+
Example Response:
|
2408
|
+
{
|
2409
|
+
"symbol": "TCS",
|
2410
|
+
"period": "annual",
|
2411
|
+
"roe": 42.5,
|
2412
|
+
"unit": "%"
|
2413
|
+
}
|
2414
|
+
"""
|
2415
|
+
params = {
|
2416
|
+
"symbol": symbol,
|
2417
|
+
"period": period,
|
2418
|
+
"consolidated": consolidated
|
2419
|
+
}
|
2420
|
+
|
2421
|
+
logger.debug("Fetching ROE for %s", symbol)
|
2422
|
+
return self._make_request("GET", self._routes["analytics.fundamentals.roe"], params=params)
|
2423
|
+
|
2424
|
+
def get_roa(
|
2425
|
+
self,
|
2426
|
+
symbol: str,
|
2427
|
+
period: str = "annual",
|
2428
|
+
consolidated: bool = True
|
2429
|
+
) -> Dict[str, Any]:
|
2430
|
+
"""
|
2431
|
+
Get Return on Assets (ROA) for a stock.
|
2432
|
+
|
2433
|
+
Args:
|
2434
|
+
symbol (str): Stock symbol.
|
2435
|
+
period (str, optional): "quarterly" or "annual". Defaults to "annual".
|
2436
|
+
consolidated (bool, optional): Use consolidated financials. Defaults to True.
|
2437
|
+
|
2438
|
+
Returns:
|
2439
|
+
Dict[str, Any]: ROA data.
|
2440
|
+
|
2441
|
+
Example Response:
|
2442
|
+
{
|
2443
|
+
"symbol": "WIPRO",
|
2444
|
+
"period": "annual",
|
2445
|
+
"roa": 14.3,
|
2446
|
+
"unit": "%"
|
2447
|
+
}
|
2448
|
+
"""
|
2449
|
+
params = {
|
2450
|
+
"symbol": symbol,
|
2451
|
+
"period": period,
|
2452
|
+
"consolidated": consolidated
|
2453
|
+
}
|
2454
|
+
|
2455
|
+
logger.debug("Fetching ROA for %s", symbol)
|
2456
|
+
return self._make_request("GET", self._routes["analytics.fundamentals.roa"], params=params)
|
2457
|
+
|
2458
|
+
def get_ebit_margin(
|
2459
|
+
self,
|
2460
|
+
symbol: str,
|
2461
|
+
period: str = "annual",
|
2462
|
+
consolidated: bool = True
|
2463
|
+
) -> Dict[str, Any]:
|
2464
|
+
"""
|
2465
|
+
Get EBIT margin for a stock.
|
2466
|
+
|
2467
|
+
Args:
|
2468
|
+
symbol (str): Stock symbol.
|
2469
|
+
period (str, optional): "quarterly" or "annual". Defaults to "annual".
|
2470
|
+
consolidated (bool, optional): Use consolidated financials. Defaults to True.
|
2471
|
+
|
2472
|
+
Returns:
|
2473
|
+
Dict[str, Any]: EBIT margin data.
|
2474
|
+
|
2475
|
+
Example Response:
|
2476
|
+
{
|
2477
|
+
"symbol": "INFY",
|
2478
|
+
"period": "annual",
|
2479
|
+
"ebit_margin": 24.1,
|
2480
|
+
"unit": "%"
|
2481
|
+
}
|
2482
|
+
"""
|
2483
|
+
params = {
|
2484
|
+
"symbol": symbol,
|
2485
|
+
"period": period,
|
2486
|
+
"consolidated": consolidated
|
2487
|
+
}
|
2488
|
+
|
2489
|
+
logger.debug("Fetching EBIT margin for %s", symbol)
|
2490
|
+
return self._make_request("GET", self._routes["analytics.fundamentals.ebit_margin"], params=params)
|
2491
|
+
|
2492
|
+
def get_ocf_netprofit_ratio(
|
2493
|
+
self,
|
2494
|
+
symbol: str,
|
2495
|
+
period: str = "annual"
|
2496
|
+
) -> Dict[str, Any]:
|
2497
|
+
"""
|
2498
|
+
Get Operating Cash Flow to Net Profit ratio.
|
2499
|
+
|
2500
|
+
Args:
|
2501
|
+
symbol (str): Stock symbol.
|
2502
|
+
period (str, optional): "annual" or "ttm". Defaults to "annual".
|
2503
|
+
|
2504
|
+
Returns:
|
2505
|
+
Dict[str, Any]: OCF/Net Profit ratio data.
|
2506
|
+
|
2507
|
+
Example Response:
|
2508
|
+
{
|
2509
|
+
"symbol": "TCS",
|
2510
|
+
"period": "annual",
|
2511
|
+
"ocf_netprofit_ratio": 1.15,
|
2512
|
+
"unit": "ratio"
|
2513
|
+
}
|
2514
|
+
|
2515
|
+
Note: Ratio > 1 indicates strong cash generation.
|
2516
|
+
"""
|
2517
|
+
params = {
|
2518
|
+
"symbol": symbol,
|
2519
|
+
"period": period
|
2520
|
+
}
|
2521
|
+
|
2522
|
+
logger.debug("Fetching OCF/Net Profit ratio for %s", symbol)
|
2523
|
+
return self._make_request("GET", self._routes["analytics.fundamentals.ocf_netprofit_ratio"], params=params)
|
2524
|
+
|
2525
|
+
def get_eps_cagr(
|
2526
|
+
self,
|
2527
|
+
symbol: str,
|
2528
|
+
start_year: int,
|
2529
|
+
end_year: int
|
2530
|
+
) -> Dict[str, Any]:
|
2531
|
+
"""
|
2532
|
+
Get EPS Compound Annual Growth Rate (CAGR).
|
2533
|
+
|
2534
|
+
Args:
|
2535
|
+
symbol (str): Stock symbol.
|
2536
|
+
start_year (int): Starting year (e.g., 2019).
|
2537
|
+
end_year (int): Ending year (e.g., 2023).
|
2538
|
+
|
2539
|
+
Returns:
|
2540
|
+
Dict[str, Any]: EPS CAGR data.
|
2541
|
+
|
2542
|
+
Example Response:
|
2543
|
+
{
|
2544
|
+
"symbol": "INFY",
|
2545
|
+
"startYear": 2019,
|
2546
|
+
"endYear": 2023,
|
2547
|
+
"epsCagr": 8.5,
|
2548
|
+
"epsStart": 35.2,
|
2549
|
+
"epsEnd": 48.7,
|
2550
|
+
"years": 4,
|
2551
|
+
"unit": "%"
|
2552
|
+
}
|
2553
|
+
"""
|
2554
|
+
params = self._normalize_params({
|
2555
|
+
"symbol": symbol,
|
2556
|
+
"startYear": start_year,
|
2557
|
+
"endYear": end_year
|
2558
|
+
})
|
2559
|
+
|
2560
|
+
logger.debug("Fetching EPS CAGR for %s from %s to %s", symbol, start_year, end_year)
|
2561
|
+
return self._make_request("GET", self._routes["analytics.fundamentals.eps_cagr"], params=params)
|
2562
|
+
|
2563
|
+
def get_book_to_market(
|
2564
|
+
self,
|
2565
|
+
symbol: str,
|
2566
|
+
as_of: str,
|
2567
|
+
price_source: str = "avgQuarter",
|
2568
|
+
custom_price: Optional[float] = None,
|
2569
|
+
standalone: bool = False,
|
2570
|
+
currency: str = "INR"
|
2571
|
+
) -> Dict[str, Any]:
|
2572
|
+
"""
|
2573
|
+
Get book-to-market ratio for a stock.
|
2574
|
+
|
2575
|
+
Args:
|
2576
|
+
symbol (str): Stock symbol (e.g., "NSE:INFY").
|
2577
|
+
as_of (str): Reference date (YYYY-MM-DD).
|
2578
|
+
price_source (str, optional): Price source: spot, avgQuarter, custom. Defaults to "avgQuarter".
|
2579
|
+
custom_price (float, optional): Required if price_source=custom.
|
2580
|
+
standalone (bool, optional): Use standalone financials. Defaults to False.
|
2581
|
+
currency (str, optional): Output currency. Defaults to "INR".
|
2582
|
+
|
2583
|
+
Returns:
|
2584
|
+
Dict[str, Any]: Book-to-market ratio data.
|
2585
|
+
|
2586
|
+
Example Response:
|
2587
|
+
{
|
2588
|
+
"symbol": "NSE:INFY",
|
2589
|
+
"asOf": "2023-03-31",
|
2590
|
+
"bookToMarket": 0.1234,
|
2591
|
+
"bookValuePerShare": 184.50,
|
2592
|
+
"marketPricePerShare": 1495.75,
|
2593
|
+
"sourcePriceType": "avgQuarter",
|
2594
|
+
"quarterRef": "Q4FY23",
|
2595
|
+
"standalone": false,
|
2596
|
+
"unit": "ratio"
|
2597
|
+
}
|
2598
|
+
"""
|
2599
|
+
params = self._normalize_params({
|
2600
|
+
"symbol": symbol,
|
2601
|
+
"asOf": as_of,
|
2602
|
+
"priceSource": price_source,
|
2603
|
+
"standalone": standalone,
|
2604
|
+
"currency": currency
|
2605
|
+
})
|
2606
|
+
|
2607
|
+
if custom_price is not None:
|
2608
|
+
params["customPrice"] = str(custom_price)
|
2609
|
+
|
2610
|
+
logger.debug("Fetching book-to-market ratio for %s", symbol)
|
2611
|
+
return self._make_request("GET", self._routes["analytics.fundamentals.book_to_market"], params=params)
|
2612
|
+
|
2613
|
+
def get_marketcap_to_sales(
|
2614
|
+
self,
|
2615
|
+
symbol: str,
|
2616
|
+
as_of: str,
|
2617
|
+
price_source: str = "avgQuarter",
|
2618
|
+
custom_price: Optional[float] = None,
|
2619
|
+
standalone: bool = False
|
2620
|
+
) -> Dict[str, Any]:
|
2621
|
+
"""
|
2622
|
+
Get market cap to sales ratio for a stock.
|
2623
|
+
|
2624
|
+
Args:
|
2625
|
+
symbol (str): Stock symbol.
|
2626
|
+
as_of (str): Reference date (YYYY-MM-DD).
|
2627
|
+
price_source (str, optional): Price source. Defaults to "avgQuarter".
|
2628
|
+
custom_price (float, optional): Custom price if price_source=custom.
|
2629
|
+
standalone (bool, optional): Use standalone financials. Defaults to False.
|
2630
|
+
|
2631
|
+
Returns:
|
2632
|
+
Dict[str, Any]: Market cap to sales ratio data.
|
2633
|
+
|
2634
|
+
Example Response:
|
2635
|
+
{
|
2636
|
+
"symbol": "INFY",
|
2637
|
+
"asOf": "2023-03-31",
|
2638
|
+
"marketcapToSales": 5.67,
|
2639
|
+
"marketCap": 615000,
|
2640
|
+
"sales": 108500,
|
2641
|
+
"pricePerShare": 1495.75,
|
2642
|
+
"sharesOutstanding": 4112000000,
|
2643
|
+
"revenueQuarter": "Q1",
|
2644
|
+
"standalone": false,
|
2645
|
+
"unit": "ratio"
|
2646
|
+
}
|
2647
|
+
"""
|
2648
|
+
params = self._normalize_params({
|
2649
|
+
"symbol": symbol,
|
2650
|
+
"asOf": as_of,
|
2651
|
+
"priceSource": price_source,
|
2652
|
+
"standalone": standalone
|
2653
|
+
})
|
2654
|
+
|
2655
|
+
if custom_price is not None:
|
2656
|
+
params["customPrice"] = str(custom_price)
|
2657
|
+
|
2658
|
+
logger.debug("Fetching market cap to sales ratio for %s", symbol)
|
2659
|
+
return self._make_request("GET", self._routes["analytics.fundamentals.marketcap_to_sales"], params=params)
|
2660
|
+
|
2661
|
+
def get_cash_to_marketcap(
|
2662
|
+
self,
|
2663
|
+
symbol: str,
|
2664
|
+
as_of: str,
|
2665
|
+
price_source: str = "avgQuarter",
|
2666
|
+
custom_price: Optional[float] = None,
|
2667
|
+
standalone: bool = False
|
2668
|
+
) -> Dict[str, Any]:
|
2669
|
+
"""
|
2670
|
+
Get cash to market cap ratio for a stock.
|
2671
|
+
|
2672
|
+
Args:
|
2673
|
+
symbol (str): Stock symbol.
|
2674
|
+
as_of (str): Reference date (YYYY-MM-DD).
|
2675
|
+
price_source (str, optional): Price source. Defaults to "avgQuarter".
|
2676
|
+
custom_price (float, optional): Custom price if price_source=custom.
|
2677
|
+
standalone (bool, optional): Use standalone financials. Defaults to False.
|
2678
|
+
|
2679
|
+
Returns:
|
2680
|
+
Dict[str, Any]: Cash to market cap ratio data.
|
2681
|
+
|
2682
|
+
Example Response:
|
2683
|
+
{
|
2684
|
+
"symbol": "INFY",
|
2685
|
+
"asOf": "2023-03-31",
|
2686
|
+
"cashToMarketcap": 0.0456,
|
2687
|
+
"cashAndEquivalents": 28050,
|
2688
|
+
"marketCap": 615000,
|
2689
|
+
"pricePerShare": 1495.75,
|
2690
|
+
"sharesOutstanding": 4112000000,
|
2691
|
+
"reportingQuarter": "Q1",
|
2692
|
+
"standalone": false,
|
2693
|
+
"unit": "ratio"
|
2694
|
+
}
|
2695
|
+
"""
|
2696
|
+
params = self._normalize_params({
|
2697
|
+
"symbol": symbol,
|
2698
|
+
"asOf": as_of,
|
2699
|
+
"priceSource": price_source,
|
2700
|
+
"standalone": standalone
|
2701
|
+
})
|
2702
|
+
|
2703
|
+
if custom_price is not None:
|
2704
|
+
params["customPrice"] = str(custom_price)
|
2705
|
+
|
2706
|
+
logger.debug("Fetching cash to market cap ratio for %s", symbol)
|
2707
|
+
return self._make_request("GET", self._routes["analytics.fundamentals.cash_to_marketcap"], params=params)
|
2708
|
+
|
2709
|
+
# --- Valuation Methods ---
|
2710
|
+
|
2711
|
+
def get_pe_ratio(
|
2712
|
+
self,
|
2713
|
+
symbol: str,
|
2714
|
+
date: Optional[str] = None,
|
2715
|
+
ttm: bool = False,
|
2716
|
+
consolidated: bool = True,
|
2717
|
+
standalone: bool = False
|
2718
|
+
) -> Dict[str, Any]:
|
2719
|
+
"""
|
2720
|
+
Get Price to Earnings (P/E) ratio.
|
2721
|
+
|
2722
|
+
Args:
|
2723
|
+
symbol (str): Stock symbol.
|
2724
|
+
date (str, optional): Date in "YYYY-MM-DD" format.
|
2725
|
+
ttm (bool, optional): Use trailing twelve months. Defaults to False.
|
2726
|
+
consolidated (bool, optional): Use consolidated financials. Defaults to True.
|
2727
|
+
standalone (bool, optional): Use standalone financials. Defaults to False.
|
2728
|
+
|
2729
|
+
Returns:
|
2730
|
+
Dict[str, Any]: P/E ratio data.
|
2731
|
+
|
2732
|
+
Example Response:
|
2733
|
+
{
|
2734
|
+
"symbol": "RELIANCE",
|
2735
|
+
"date": "2025-07-31",
|
2736
|
+
"pe_ratio": 27.5,
|
2737
|
+
"price": null,
|
2738
|
+
"eps": null,
|
2739
|
+
"ttm": false,
|
2740
|
+
"consolidated": true,
|
2741
|
+
"unit": "ratio"
|
2742
|
+
}
|
2743
|
+
"""
|
2744
|
+
params = {
|
2745
|
+
"symbol": symbol,
|
2746
|
+
"ttm": ttm,
|
2747
|
+
"consolidated": consolidated,
|
2748
|
+
"standalone": standalone
|
2749
|
+
}
|
2750
|
+
|
2751
|
+
if date:
|
2752
|
+
params["date"] = date
|
2753
|
+
|
2754
|
+
logger.debug("Fetching P/E ratio for %s", symbol)
|
2755
|
+
return self._make_request("GET", self._routes["analytics.valuation.pe_ratio"], params=params)
|
2756
|
+
|
2757
|
+
def get_pb_ratio(
|
2758
|
+
self,
|
2759
|
+
symbol: str,
|
2760
|
+
date: Optional[str] = None,
|
2761
|
+
consolidated: bool = True,
|
2762
|
+
standalone: bool = False
|
2763
|
+
) -> Dict[str, Any]:
|
2764
|
+
"""
|
2765
|
+
Get Price to Book (P/B) ratio.
|
2766
|
+
|
2767
|
+
Args:
|
2768
|
+
symbol (str): Stock symbol.
|
2769
|
+
date (str, optional): Date in "YYYY-MM-DD" format.
|
2770
|
+
consolidated (bool, optional): Use consolidated financials. Defaults to True.
|
2771
|
+
standalone (bool, optional): Use standalone financials. Defaults to False.
|
2772
|
+
|
2773
|
+
Returns:
|
2774
|
+
Dict[str, Any]: P/B ratio data.
|
2775
|
+
|
2776
|
+
Example Response:
|
2777
|
+
{
|
2778
|
+
"symbol": "HDFC",
|
2779
|
+
"date": "2025-07-31",
|
2780
|
+
"pb_ratio": 3.2,
|
2781
|
+
"price": null,
|
2782
|
+
"book_value": null,
|
2783
|
+
"consolidated": true,
|
2784
|
+
"unit": "ratio"
|
2785
|
+
}
|
2786
|
+
"""
|
2787
|
+
params = {
|
2788
|
+
"symbol": symbol,
|
2789
|
+
"consolidated": consolidated,
|
2790
|
+
"standalone": standalone
|
2791
|
+
}
|
2792
|
+
|
2793
|
+
if date:
|
2794
|
+
params["date"] = date
|
2795
|
+
|
2796
|
+
logger.debug("Fetching P/B ratio for %s", symbol)
|
2797
|
+
return self._make_request("GET", self._routes["analytics.valuation.pb_ratio"], params=params)
|
2798
|
+
|
2799
|
+
def get_ev_ebitda(
|
2800
|
+
self,
|
2801
|
+
symbol: str,
|
2802
|
+
date: Optional[str] = None,
|
2803
|
+
ttm: bool = False,
|
2804
|
+
consolidated: bool = True,
|
2805
|
+
standalone: bool = False
|
2806
|
+
) -> Dict[str, Any]:
|
2807
|
+
"""
|
2808
|
+
Get Enterprise Value to EBITDA ratio.
|
2809
|
+
|
2810
|
+
Args:
|
2811
|
+
symbol (str): Stock symbol.
|
2812
|
+
date (str, optional): Date in "YYYY-MM-DD" format.
|
2813
|
+
ttm (bool, optional): Use trailing twelve months. Defaults to False.
|
2814
|
+
consolidated (bool, optional): Use consolidated financials. Defaults to True.
|
2815
|
+
standalone (bool, optional): Use standalone financials. Defaults to False.
|
2816
|
+
|
2817
|
+
Returns:
|
2818
|
+
Dict[str, Any]: EV/EBITDA ratio data.
|
2819
|
+
|
2820
|
+
Example Response:
|
2821
|
+
{
|
2822
|
+
"symbol": "LTI",
|
2823
|
+
"date": "2025-06-30",
|
2824
|
+
"ev_ebitda": 22.8,
|
2825
|
+
"enterprise_value": null,
|
2826
|
+
"ebitda": null,
|
2827
|
+
"ttm": false,
|
2828
|
+
"unit": "ratio"
|
2829
|
+
}
|
2830
|
+
"""
|
2831
|
+
params = {
|
2832
|
+
"symbol": symbol,
|
2833
|
+
"ttm": ttm,
|
2834
|
+
"consolidated": consolidated,
|
2835
|
+
"standalone": standalone
|
2836
|
+
}
|
2837
|
+
|
2838
|
+
if date:
|
2839
|
+
params["date"] = date
|
2840
|
+
|
2841
|
+
logger.debug("Fetching EV/EBITDA for %s", symbol)
|
2842
|
+
return self._make_request("GET", self._routes["analytics.valuation.ev_ebitda"], params=params)
|
2843
|
+
|
2844
|
+
def get_fcf_yield(
|
2845
|
+
self,
|
2846
|
+
symbol: str,
|
2847
|
+
date: Optional[str] = None,
|
2848
|
+
ttm: bool = False,
|
2849
|
+
consolidated: bool = True,
|
2850
|
+
standalone: bool = False
|
2851
|
+
) -> Dict[str, Any]:
|
2852
|
+
"""
|
2853
|
+
Get Free Cash Flow yield.
|
2854
|
+
|
2855
|
+
Args:
|
2856
|
+
symbol (str): Stock symbol.
|
2857
|
+
date (str, optional): Date in "YYYY-MM-DD" format.
|
2858
|
+
ttm (bool, optional): Use trailing twelve months. Defaults to False.
|
2859
|
+
consolidated (bool, optional): Use consolidated financials. Defaults to True.
|
2860
|
+
standalone (bool, optional): Use standalone financials. Defaults to False.
|
2861
|
+
|
2862
|
+
Returns:
|
2863
|
+
Dict[str, Any]: FCF yield data.
|
2864
|
+
|
2865
|
+
Example Response:
|
2866
|
+
{
|
2867
|
+
"symbol": "HDFCAMC",
|
2868
|
+
"date": "2025-06-30",
|
2869
|
+
"fcf_yield": 4.5,
|
2870
|
+
"fcf": null,
|
2871
|
+
"market_cap": null,
|
2872
|
+
"ttm": false,
|
2873
|
+
"unit": "%"
|
2874
|
+
}
|
2875
|
+
"""
|
2876
|
+
params = {
|
2877
|
+
"symbol": symbol,
|
2878
|
+
"ttm": ttm,
|
2879
|
+
"consolidated": consolidated,
|
2880
|
+
"standalone": standalone
|
2881
|
+
}
|
2882
|
+
|
2883
|
+
if date:
|
2884
|
+
params["date"] = date
|
2885
|
+
|
2886
|
+
logger.debug("Fetching FCF yield for %s", symbol)
|
2887
|
+
return self._make_request("GET", self._routes["analytics.valuation.fcf_yield"], params=params)
|
2888
|
+
|
2889
|
+
# --- Returns Methods ---
|
2890
|
+
|
2891
|
+
|
2892
|
+
def get_quarterly_returns(
|
2893
|
+
self,
|
2894
|
+
symbol: str,
|
2895
|
+
start_date: str,
|
2896
|
+
end_date: str,
|
2897
|
+
adjusted: bool = True
|
2898
|
+
) -> Dict[str, Any]:
|
2899
|
+
"""
|
2900
|
+
Get quarterly returns for a stock.
|
2901
|
+
|
2902
|
+
Args:
|
2903
|
+
symbol (str): Stock symbol.
|
2904
|
+
start_date (str): Start date in "YYYY-MM-DD" format.
|
2905
|
+
end_date (str): End date in "YYYY-MM-DD" format.
|
2906
|
+
adjusted (bool, optional): Use adjusted prices. Defaults to True.
|
2907
|
+
|
2908
|
+
Returns:
|
2909
|
+
Dict[str, Any]: Quarterly returns data.
|
2910
|
+
"""
|
2911
|
+
params = self._normalize_params({
|
2912
|
+
"symbol": symbol,
|
2913
|
+
"startDate": start_date,
|
2914
|
+
"endDate": end_date,
|
2915
|
+
"adjusted": adjusted
|
2916
|
+
})
|
2917
|
+
|
2918
|
+
logger.debug("Fetching quarterly returns for %s", symbol)
|
2919
|
+
return self._make_request("GET", self._routes["analytics.returns.quarterly"], params=params)
|
2920
|
+
|
2921
|
+
def get_monthly_returns(
|
2922
|
+
self,
|
2923
|
+
symbol: str,
|
2924
|
+
start_date: str,
|
2925
|
+
end_date: str,
|
2926
|
+
adjusted: bool = True
|
2927
|
+
) -> Dict[str, Any]:
|
2928
|
+
"""
|
2929
|
+
Get monthly returns for a stock.
|
2930
|
+
|
2931
|
+
Args:
|
2932
|
+
symbol (str): Stock symbol.
|
2933
|
+
start_date (str): Start date in "YYYY-MM-DD" format.
|
2934
|
+
end_date (str): End date in "YYYY-MM-DD" format.
|
2935
|
+
adjusted (bool, optional): Use adjusted prices. Defaults to True.
|
2936
|
+
|
2937
|
+
Returns:
|
2938
|
+
Dict[str, Any]: Monthly returns data.
|
2939
|
+
"""
|
2940
|
+
params = self._normalize_params({
|
2941
|
+
"symbol": symbol,
|
2942
|
+
"startDate": start_date,
|
2943
|
+
"endDate": end_date,
|
2944
|
+
"adjusted": adjusted
|
2945
|
+
})
|
2946
|
+
|
2947
|
+
logger.debug("Fetching monthly returns for %s", symbol)
|
2948
|
+
return self._make_request("GET", self._routes["analytics.returns.monthly"], params=params)
|
2949
|
+
|
2950
|
+
# --- Market Data Methods ---
|
2951
|
+
|
2952
|
+
def get_analytics_ohlcv_daily(
|
2953
|
+
self,
|
2954
|
+
symbol: str,
|
2955
|
+
start_date: str,
|
2956
|
+
end_date: str,
|
2957
|
+
adjusted: bool = True
|
2958
|
+
) -> Dict[str, Any]:
|
2959
|
+
"""
|
2960
|
+
Get daily OHLCV data from analytics API.
|
2961
|
+
|
2962
|
+
Args:
|
2963
|
+
symbol (str): Stock symbol.
|
2964
|
+
start_date (str): Start date in "YYYY-MM-DD" format.
|
2965
|
+
end_date (str): End date in "YYYY-MM-DD" format.
|
2966
|
+
adjusted (bool, optional): Use adjusted prices. Defaults to True (ignored as adjusted data not available).
|
2967
|
+
|
2968
|
+
Returns:
|
2969
|
+
Dict[str, Any]: Daily OHLCV data.
|
2970
|
+
|
2971
|
+
Example Response:
|
2972
|
+
{
|
2973
|
+
"data": [
|
2974
|
+
{
|
2975
|
+
"date": "2025-01-01",
|
2976
|
+
"open": 2305.0,
|
2977
|
+
"high": 2340.0,
|
2978
|
+
"low": 2290.0,
|
2979
|
+
"close": 2325.0,
|
2980
|
+
"volume": 12500000,
|
2981
|
+
"symbol": "RELIANCE"
|
2982
|
+
}
|
2983
|
+
]
|
2984
|
+
}
|
2985
|
+
|
2986
|
+
Note: Maximum 365 days per request.
|
2987
|
+
"""
|
2988
|
+
params = self._normalize_params({
|
2989
|
+
"symbol": symbol,
|
2990
|
+
"startDate": start_date,
|
2991
|
+
"endDate": end_date,
|
2992
|
+
"adjusted": adjusted
|
2993
|
+
})
|
2994
|
+
|
2995
|
+
logger.debug("Fetching analytics OHLCV daily data for %s", symbol)
|
2996
|
+
return self._make_request("GET", self._routes["analytics.marketdata.ohlcv_daily"], params=params)
|
2997
|
+
|
2998
|
+
def get_analytics_historical_prices(
|
2999
|
+
self,
|
3000
|
+
symbol: str,
|
3001
|
+
start_date: str,
|
3002
|
+
end_date: str,
|
3003
|
+
adjusted: bool = True
|
3004
|
+
) -> Dict[str, Any]:
|
3005
|
+
"""
|
3006
|
+
Get historical prices from analytics API (same as OHLCV daily).
|
3007
|
+
|
3008
|
+
Args:
|
3009
|
+
symbol (str): Stock symbol.
|
3010
|
+
start_date (str): Start date in "YYYY-MM-DD" format.
|
3011
|
+
end_date (str): End date in "YYYY-MM-DD" format.
|
3012
|
+
adjusted (bool, optional): Use adjusted prices. Defaults to True.
|
3013
|
+
|
3014
|
+
Returns:
|
3015
|
+
Dict[str, Any]: Historical price data.
|
3016
|
+
"""
|
3017
|
+
params = self._normalize_params({
|
3018
|
+
"symbol": symbol,
|
3019
|
+
"startDate": start_date,
|
3020
|
+
"endDate": end_date,
|
3021
|
+
"adjusted": adjusted
|
3022
|
+
})
|
3023
|
+
|
3024
|
+
logger.debug("Fetching analytics historical prices for %s", symbol)
|
3025
|
+
return self._make_request("GET", self._routes["analytics.marketdata.historical_prices"], params=params)
|
3026
|
+
|
3027
|
+
def get_free_float_market_cap(
|
3028
|
+
self,
|
3029
|
+
symbol: str,
|
3030
|
+
date: Optional[str] = None
|
3031
|
+
) -> Dict[str, Any]:
|
3032
|
+
"""
|
3033
|
+
Get free-float market capitalization.
|
3034
|
+
|
3035
|
+
Args:
|
3036
|
+
symbol (str): Stock symbol.
|
3037
|
+
date (str, optional): Date in "YYYY-MM-DD" format. Defaults to most recent.
|
3038
|
+
|
3039
|
+
Returns:
|
3040
|
+
Dict[str, Any]: Free-float market cap data.
|
3041
|
+
|
3042
|
+
Example Response:
|
3043
|
+
{
|
3044
|
+
"symbol": "RELIANCE",
|
3045
|
+
"date": "2025-07-31",
|
3046
|
+
"free_float_market_cap": 1054321.89,
|
3047
|
+
"market_cap": 1234567.89,
|
3048
|
+
"promoter_holding_percent": 14.6,
|
3049
|
+
"unit": "₹ Crores"
|
3050
|
+
}
|
3051
|
+
"""
|
3052
|
+
params = {"symbol": symbol}
|
3053
|
+
|
3054
|
+
if date:
|
3055
|
+
params["date"] = date
|
3056
|
+
|
3057
|
+
logger.debug("Fetching free-float market cap for %s", symbol)
|
3058
|
+
return self._make_request("GET", self._routes["analytics.marketdata.free_float_market_cap"], params=params)
|
3059
|
+
|
3060
|
+
# --- Ownership Methods ---
|
3061
|
+
|
3062
|
+
def get_fii_dii_holdings(
|
3063
|
+
self,
|
3064
|
+
symbol: str,
|
3065
|
+
quarter: Optional[str] = None
|
3066
|
+
) -> Dict[str, Any]:
|
3067
|
+
"""
|
3068
|
+
Get FII and DII holdings percentages.
|
3069
|
+
|
3070
|
+
Args:
|
3071
|
+
symbol (str): Stock symbol.
|
3072
|
+
quarter (str, optional): Quarter in "Q1FY24" format. Defaults to latest.
|
3073
|
+
|
3074
|
+
Returns:
|
3075
|
+
Dict[str, Any]: FII and DII holdings data.
|
3076
|
+
|
3077
|
+
Example Response:
|
3078
|
+
{
|
3079
|
+
"symbol": "RELIANCE",
|
3080
|
+
"quarter": "Q4FY23",
|
3081
|
+
"fii_percentage": 24.3,
|
3082
|
+
"dii_percentage": 18.7,
|
3083
|
+
"institutional_total": 43.0,
|
3084
|
+
"unit": "%"
|
3085
|
+
}
|
3086
|
+
"""
|
3087
|
+
params = {"symbol": symbol}
|
3088
|
+
|
3089
|
+
if quarter:
|
3090
|
+
params["quarter"] = quarter
|
3091
|
+
|
3092
|
+
logger.debug("Fetching FII/DII holdings for %s", symbol)
|
3093
|
+
return self._make_request("GET", self._routes["analytics.ownership.fii_dii"], params=params)
|
3094
|
+
|
3095
|
+
def get_fii_change(self, symbol: str) -> Dict[str, Any]:
|
3096
|
+
"""
|
3097
|
+
Get FII holding change from previous quarter.
|
3098
|
+
|
3099
|
+
Args:
|
3100
|
+
symbol (str): Stock symbol.
|
3101
|
+
|
3102
|
+
Returns:
|
3103
|
+
Dict[str, Any]: FII change data.
|
3104
|
+
|
3105
|
+
Example Response:
|
3106
|
+
{
|
3107
|
+
"symbol": "INFY",
|
3108
|
+
"quarter": "2025-03-31",
|
3109
|
+
"fii_change": 1.2,
|
3110
|
+
"current_fii": 33.5,
|
3111
|
+
"previous_fii": 32.3,
|
3112
|
+
"unit": "%"
|
3113
|
+
}
|
3114
|
+
"""
|
3115
|
+
params = {"symbol": symbol}
|
3116
|
+
|
3117
|
+
logger.debug("Fetching FII change for %s", symbol)
|
3118
|
+
return self._make_request("GET", self._routes["analytics.ownership.fii_change"], params=params)
|
3119
|
+
|
3120
|
+
def get_dii_change(self, symbol: str) -> Dict[str, Any]:
|
3121
|
+
"""
|
3122
|
+
Get DII holding change from previous quarter.
|
3123
|
+
|
3124
|
+
Args:
|
3125
|
+
symbol (str): Stock symbol.
|
3126
|
+
|
3127
|
+
Returns:
|
3128
|
+
Dict[str, Any]: DII change data.
|
3129
|
+
|
3130
|
+
Example Response:
|
3131
|
+
{
|
3132
|
+
"symbol": "INFY",
|
3133
|
+
"quarter": "2025-03-31",
|
3134
|
+
"dii_change": -0.5,
|
3135
|
+
"current_dii": 15.2,
|
3136
|
+
"previous_dii": 15.7,
|
3137
|
+
"unit": "%"
|
3138
|
+
}
|
3139
|
+
"""
|
3140
|
+
params = {"symbol": symbol}
|
3141
|
+
|
3142
|
+
logger.debug("Fetching DII change for %s", symbol)
|
3143
|
+
return self._make_request("GET", self._routes["analytics.ownership.dii_change"], params=params)
|
3144
|
+
|
3145
|
+
# --- Index Data Methods ---
|
3146
|
+
|
3147
|
+
def get_index_ohlc_daily(
|
3148
|
+
self,
|
3149
|
+
symbol: str,
|
3150
|
+
start_date: str,
|
3151
|
+
end_date: str
|
3152
|
+
) -> Dict[str, Any]:
|
3153
|
+
"""
|
3154
|
+
Get daily OHLC data for an index.
|
3155
|
+
|
3156
|
+
Args:
|
3157
|
+
symbol (str): Index symbol (e.g., "NIFTY50").
|
3158
|
+
start_date (str): Start date in "YYYY-MM-DD" format.
|
3159
|
+
end_date (str): End date in "YYYY-MM-DD" format.
|
3160
|
+
|
3161
|
+
Returns:
|
3162
|
+
Dict[str, Any]: Index OHLC data.
|
3163
|
+
|
3164
|
+
Example Response:
|
3165
|
+
{
|
3166
|
+
"data": [
|
3167
|
+
{
|
3168
|
+
"date": "2025-01-01",
|
3169
|
+
"open": 18250.0,
|
3170
|
+
"high": 18350.0,
|
3171
|
+
"low": 18200.0,
|
3172
|
+
"close": 18325.0,
|
3173
|
+
"symbol": "NIFTY50"
|
3174
|
+
}
|
3175
|
+
]
|
3176
|
+
}
|
3177
|
+
"""
|
3178
|
+
params = self._normalize_params({
|
3179
|
+
"index": symbol,
|
3180
|
+
"startDate": start_date,
|
3181
|
+
"endDate": end_date
|
3182
|
+
})
|
3183
|
+
|
3184
|
+
logger.debug("Fetching index OHLC daily data for %s", symbol)
|
3185
|
+
return self._make_request("GET", self._routes["analytics.marketdata.index_ohlc_daily"], params=params)
|
3186
|
+
|
3187
|
+
# --- Metrics Methods ---
|
3188
|
+
|
3189
|
+
def get_sortino_ratio(
|
3190
|
+
self,
|
3191
|
+
symbol: str,
|
3192
|
+
start_date: str,
|
3193
|
+
end_date: str,
|
3194
|
+
rf: float = 0.065,
|
3195
|
+
interval: str = "daily"
|
3196
|
+
) -> Dict[str, Any]:
|
3197
|
+
"""
|
3198
|
+
Get Sortino ratio for a stock/strategy/index.
|
3199
|
+
|
3200
|
+
Args:
|
3201
|
+
symbol (str): Stock/strategy/index symbol.
|
3202
|
+
start_date (str): Start date in "YYYY-MM-DD" format.
|
3203
|
+
end_date (str): End date in "YYYY-MM-DD" format.
|
3204
|
+
rf (float, optional): Annualized risk-free rate. Defaults to 0.065.
|
3205
|
+
interval (str, optional): Return frequency: daily, weekly, monthly. Defaults to "daily".
|
3206
|
+
|
3207
|
+
Returns:
|
3208
|
+
Dict[str, Any]: Sortino ratio data.
|
3209
|
+
|
3210
|
+
Example Response:
|
3211
|
+
{
|
3212
|
+
"symbol": "HDFCBANK",
|
3213
|
+
"startDate": "2024-01-01",
|
3214
|
+
"endDate": "2025-01-01",
|
3215
|
+
"interval": "daily",
|
3216
|
+
"rf": 0.065,
|
3217
|
+
"sortinoRatio": 1.8542,
|
3218
|
+
"annualizedReturn": 0.1234,
|
3219
|
+
"downsideDeviation": 0.0321,
|
3220
|
+
"unit": "ratio"
|
3221
|
+
}
|
3222
|
+
"""
|
3223
|
+
params = self._normalize_params({
|
3224
|
+
"symbol": symbol,
|
3225
|
+
"startDate": start_date,
|
3226
|
+
"endDate": end_date,
|
3227
|
+
"rf": rf,
|
3228
|
+
"interval": interval
|
3229
|
+
})
|
3230
|
+
|
3231
|
+
logger.debug("Fetching Sortino ratio for %s", symbol)
|
3232
|
+
return self._make_request("GET", self._routes["analytics.metrics.sortino_ratio"], params=params)
|
3233
|
+
|
3234
|
+
def get_upside_capture(
|
3235
|
+
self,
|
3236
|
+
symbol: str,
|
3237
|
+
benchmark_symbol: str,
|
3238
|
+
start_date: str,
|
3239
|
+
end_date: str,
|
3240
|
+
interval: str = "daily"
|
3241
|
+
) -> Dict[str, Any]:
|
3242
|
+
"""
|
3243
|
+
Get upside capture ratio for a stock/portfolio.
|
3244
|
+
|
3245
|
+
Args:
|
3246
|
+
symbol (str): Stock/portfolio symbol.
|
3247
|
+
benchmark_symbol (str): Benchmark symbol.
|
3248
|
+
start_date (str): Start date in "YYYY-MM-DD" format.
|
3249
|
+
end_date (str): End date in "YYYY-MM-DD" format.
|
3250
|
+
interval (str, optional): Comparison interval. Defaults to "daily".
|
3251
|
+
|
3252
|
+
Returns:
|
3253
|
+
Dict[str, Any]: Upside capture ratio data.
|
3254
|
+
|
3255
|
+
Example Response:
|
3256
|
+
{
|
3257
|
+
"symbol": "ICICIBANK",
|
3258
|
+
"benchmarkSymbol": "NIFTY50",
|
3259
|
+
"startDate": "2024-01-01",
|
3260
|
+
"endDate": "2025-01-01",
|
3261
|
+
"interval": "daily",
|
3262
|
+
"upsideCaptureRatio": 112.50,
|
3263
|
+
"periodsAnalyzed": 250,
|
3264
|
+
"positiveBenchmarkPeriods": 135,
|
3265
|
+
"unit": "%"
|
3266
|
+
}
|
3267
|
+
"""
|
3268
|
+
params = self._normalize_params({
|
3269
|
+
"symbol": symbol,
|
3270
|
+
"benchmarkSymbol": benchmark_symbol,
|
3271
|
+
"startDate": start_date,
|
3272
|
+
"endDate": end_date,
|
3273
|
+
"interval": interval
|
3274
|
+
})
|
3275
|
+
|
3276
|
+
logger.debug("Fetching upside capture ratio for %s vs %s", symbol, benchmark_symbol)
|
3277
|
+
return self._make_request("GET", self._routes["analytics.metrics.upside_capture"], params=params)
|
3278
|
+
|
3279
|
+
# --- Macro Methods ---
|
3280
|
+
|
3281
|
+
def get_risk_free_rate(
|
3282
|
+
self,
|
3283
|
+
start_date: str,
|
3284
|
+
end_date: str,
|
3285
|
+
tenor: str = "10Y",
|
3286
|
+
country: str = "IN",
|
3287
|
+
method: str = "average"
|
3288
|
+
) -> Dict[str, Any]:
|
3289
|
+
"""
|
3290
|
+
Get risk-free rates for various tenors and countries.
|
3291
|
+
|
3292
|
+
Args:
|
3293
|
+
start_date (str): Start date in "YYYY-MM-DD" format.
|
3294
|
+
end_date (str): End date in "YYYY-MM-DD" format.
|
3295
|
+
tenor (str, optional): Maturity: 3M, 6M, 1Y, 5Y, 10Y. Defaults to "10Y".
|
3296
|
+
country (str, optional): Country code. Defaults to "IN".
|
3297
|
+
method (str, optional): Calculation: average, start, end, daily_series. Defaults to "average".
|
3298
|
+
|
3299
|
+
Returns:
|
3300
|
+
Dict[str, Any]: Risk-free rate data.
|
3301
|
+
|
3302
|
+
Example Response:
|
3303
|
+
{
|
3304
|
+
"country": "IN",
|
3305
|
+
"tenor": "10Y",
|
3306
|
+
"startDate": "2024-01-01",
|
3307
|
+
"endDate": "2024-12-31",
|
3308
|
+
"method": "average",
|
3309
|
+
"riskFreeRate": 0.0735,
|
3310
|
+
"source": "RBI/FIMMDA (Default)",
|
3311
|
+
"unit": "decimal"
|
3312
|
+
}
|
3313
|
+
"""
|
3314
|
+
params = self._normalize_params({
|
3315
|
+
"startDate": start_date,
|
3316
|
+
"endDate": end_date,
|
3317
|
+
"tenor": tenor,
|
3318
|
+
"country": country,
|
3319
|
+
"method": method
|
3320
|
+
})
|
3321
|
+
|
3322
|
+
logger.debug("Fetching risk-free rate for %s %s", country, tenor)
|
3323
|
+
return self._make_request("GET", self._routes["analytics.macro.risk_free_rate"], params=params)
|
3324
|
+
|
3325
|
+
# --- Risk Analysis Methods ---
|
3326
|
+
|
3327
|
+
def get_max_drawdown(
|
3328
|
+
self,
|
3329
|
+
symbol: str,
|
3330
|
+
start_date: str,
|
3331
|
+
end_date: str,
|
3332
|
+
adjusted: bool = True,
|
3333
|
+
interval: str = "daily"
|
3334
|
+
) -> Dict[str, Any]:
|
3335
|
+
"""
|
3336
|
+
Calculate maximum drawdown for a stock over a specified period.
|
3337
|
+
|
3338
|
+
Args:
|
3339
|
+
symbol (str): Stock symbol.
|
3340
|
+
start_date (str): Analysis start date (YYYY-MM-DD).
|
3341
|
+
end_date (str): Analysis end date (YYYY-MM-DD).
|
3342
|
+
adjusted (bool, optional): Use adjusted prices. Defaults to True.
|
3343
|
+
interval (str, optional): Data frequency: daily, weekly, monthly. Defaults to "daily".
|
3344
|
+
|
3345
|
+
Returns:
|
3346
|
+
Dict[str, Any]: Maximum drawdown data.
|
3347
|
+
|
3348
|
+
Example Response:
|
3349
|
+
{
|
3350
|
+
"symbol": "TCS",
|
3351
|
+
"startDate": "2024-01-01",
|
3352
|
+
"endDate": "2024-12-31",
|
3353
|
+
"adjusted": true,
|
3354
|
+
"interval": "daily",
|
3355
|
+
"maxDrawdown": -0.1847,
|
3356
|
+
"peakDate": "2024-03-15",
|
3357
|
+
"troughDate": "2024-06-08",
|
3358
|
+
"peakPrice": 4250.50,
|
3359
|
+
"troughPrice": 3465.75,
|
3360
|
+
"unit": "decimal"
|
3361
|
+
}
|
3362
|
+
"""
|
3363
|
+
params = self._normalize_params({
|
3364
|
+
"symbol": symbol,
|
3365
|
+
"startDate": start_date,
|
3366
|
+
"endDate": end_date,
|
3367
|
+
"adjusted": adjusted,
|
3368
|
+
"interval": interval
|
3369
|
+
})
|
3370
|
+
|
3371
|
+
logger.debug("Fetching max drawdown for %s", symbol)
|
3372
|
+
return self._make_request("GET", self._routes["analytics.risk.max_drawdown"], params=params)
|
3373
|
+
|
3374
|
+
def get_returns_volatility(
|
3375
|
+
self,
|
3376
|
+
symbol: str,
|
3377
|
+
start_date: str,
|
3378
|
+
end_date: str,
|
3379
|
+
frequency: str = "daily",
|
3380
|
+
periods: Optional[int] = None
|
3381
|
+
) -> Dict[str, Any]:
|
3382
|
+
"""
|
3383
|
+
Calculate returns volatility using standard deviation over a specified period.
|
3384
|
+
|
3385
|
+
Args:
|
3386
|
+
symbol (str): Stock symbol.
|
3387
|
+
start_date (str): Analysis start date (YYYY-MM-DD).
|
3388
|
+
end_date (str): Analysis end date (YYYY-MM-DD).
|
3389
|
+
frequency (str, optional): Return frequency: daily, weekly, monthly. Defaults to "daily".
|
3390
|
+
periods (int, optional): Rolling window size (5-250). Auto-calculated if not provided.
|
3391
|
+
|
3392
|
+
Returns:
|
3393
|
+
Dict[str, Any]: Volatility analysis data.
|
3394
|
+
|
3395
|
+
Example Response:
|
3396
|
+
{
|
3397
|
+
"symbol": "INFY",
|
3398
|
+
"startDate": "2024-01-01",
|
3399
|
+
"endDate": "2024-06-30",
|
3400
|
+
"frequency": "daily",
|
3401
|
+
"volatility": 0.0243,
|
3402
|
+
"annualizedVolatility": 0.3856,
|
3403
|
+
"periods": 125,
|
3404
|
+
"unit": "decimal"
|
3405
|
+
}
|
3406
|
+
"""
|
3407
|
+
params = self._normalize_params({
|
3408
|
+
"symbol": symbol,
|
3409
|
+
"startDate": start_date,
|
3410
|
+
"endDate": end_date,
|
3411
|
+
"frequency": frequency
|
3412
|
+
})
|
3413
|
+
|
3414
|
+
if periods is not None:
|
3415
|
+
params["periods"] = str(periods)
|
3416
|
+
|
3417
|
+
logger.debug("Fetching returns volatility for %s", symbol)
|
3418
|
+
return self._make_request("GET", self._routes["analytics.risk.returns_volatility"], params=params)
|
3419
|
+
|
3420
|
+
# --- Metadata Methods ---
|
3421
|
+
|
3422
|
+
def get_sector_classification(
|
3423
|
+
self,
|
3424
|
+
symbol: str
|
3425
|
+
) -> Dict[str, Any]:
|
3426
|
+
"""
|
3427
|
+
Get comprehensive sector and industry classification for a stock.
|
3428
|
+
|
3429
|
+
Args:
|
3430
|
+
symbol (str): Stock symbol.
|
3431
|
+
|
3432
|
+
Returns:
|
3433
|
+
Dict[str, Any]: Sector classification data.
|
3434
|
+
|
3435
|
+
Example Response:
|
3436
|
+
{
|
3437
|
+
"symbol": "TATASTEEL",
|
3438
|
+
"sector": "Basic Materials",
|
3439
|
+
"industry": "Steel",
|
3440
|
+
"subIndustry": "Integrated Steel",
|
3441
|
+
"classification": "Industrial Metals & Mining",
|
3442
|
+
"lastUpdated": "2024-01-15"
|
3443
|
+
}
|
3444
|
+
"""
|
3445
|
+
params = self._normalize_params({
|
3446
|
+
"symbol": symbol
|
3447
|
+
})
|
3448
|
+
|
3449
|
+
logger.debug("Fetching sector classification for %s", symbol)
|
3450
|
+
return self._make_request("GET", self._routes["analytics.metadata.sector"], params=params)
|
3451
|
+
|
3452
|
+
# --- Leverage Analysis Methods ---
|
3453
|
+
|
3454
|
+
def get_debt_equity_ratio(
|
3455
|
+
self,
|
3456
|
+
symbol: str,
|
3457
|
+
date: Optional[str] = None,
|
3458
|
+
consolidated: bool = True
|
3459
|
+
) -> Dict[str, Any]:
|
3460
|
+
"""
|
3461
|
+
Calculate debt-to-equity ratio using most recent financial data.
|
3462
|
+
|
3463
|
+
Args:
|
3464
|
+
symbol (str): Stock symbol.
|
3465
|
+
date (str, optional): Specific quarter-end date (YYYY-MM-DD). Latest if not provided.
|
3466
|
+
consolidated (bool, optional): Use consolidated financials. Defaults to True.
|
3467
|
+
|
3468
|
+
Returns:
|
3469
|
+
Dict[str, Any]: Debt-to-equity ratio data.
|
3470
|
+
|
3471
|
+
Example Response:
|
3472
|
+
{
|
3473
|
+
"symbol": "RELIANCE",
|
3474
|
+
"date": "2024-09-30",
|
3475
|
+
"deRatio": 0.3247,
|
3476
|
+
"totalDebt": 287450.25,
|
3477
|
+
"shareholderEquity": 884972.10,
|
3478
|
+
"source": "consolidated",
|
3479
|
+
"quarter": "Q2FY25",
|
3480
|
+
"unit": "ratio"
|
3481
|
+
}
|
3482
|
+
"""
|
3483
|
+
params = self._normalize_params({
|
3484
|
+
"symbol": symbol,
|
3485
|
+
"consolidated": consolidated
|
3486
|
+
})
|
3487
|
+
|
3488
|
+
if date:
|
3489
|
+
params["date"] = date
|
3490
|
+
|
3491
|
+
logger.debug("Fetching debt-to-equity ratio for %s", symbol)
|
3492
|
+
return self._make_request("GET", self._routes["analytics.leverage.debt_equity_ratio"], params=params)
|
3493
|
+
|
3494
|
+
def get_cagr(
|
3495
|
+
self,
|
3496
|
+
symbol: str,
|
3497
|
+
start_date: str,
|
3498
|
+
end_date: str,
|
3499
|
+
adjusted: bool = True
|
3500
|
+
) -> Dict[str, Any]:
|
3501
|
+
"""
|
3502
|
+
Calculate Compound Annual Growth Rate (CAGR) over a specified time period.
|
3503
|
+
|
3504
|
+
Args:
|
3505
|
+
symbol (str): Stock symbol.
|
3506
|
+
start_date (str): Investment start date (YYYY-MM-DD).
|
3507
|
+
end_date (str): Investment end date (YYYY-MM-DD).
|
3508
|
+
adjusted (bool, optional): Use adjusted prices. Defaults to True.
|
3509
|
+
|
3510
|
+
Returns:
|
3511
|
+
Dict[str, Any]: CAGR calculation data.
|
3512
|
+
|
3513
|
+
Example Response:
|
3514
|
+
{
|
3515
|
+
"symbol": "TCS",
|
3516
|
+
"startDate": "2020-01-01",
|
3517
|
+
"endDate": "2024-12-31",
|
3518
|
+
"cagr": 12.45,
|
3519
|
+
"startPrice": 2150.30,
|
3520
|
+
"endPrice": 3847.60,
|
3521
|
+
"years": 5.0,
|
3522
|
+
"adjusted": true,
|
3523
|
+
"unit": "%"
|
3524
|
+
}
|
3525
|
+
"""
|
3526
|
+
params = self._normalize_params({
|
3527
|
+
"symbol": symbol,
|
3528
|
+
"startDate": start_date,
|
3529
|
+
"endDate": end_date,
|
3530
|
+
"adjusted": adjusted
|
3531
|
+
})
|
3532
|
+
|
3533
|
+
logger.debug("Fetching CAGR for %s from %s to %s", symbol, start_date, end_date)
|
3534
|
+
return self._make_request("GET", self._routes["analytics.returns.cagr"], params=params)
|
3535
|
+
|
3536
|
+
# --- New Analytics Methods ---
|
3537
|
+
|
3538
|
+
def get_average_traded_volume(
|
3539
|
+
self,
|
3540
|
+
symbol: str,
|
3541
|
+
start_date: str,
|
3542
|
+
end_date: str,
|
3543
|
+
interval: str = "daily"
|
3544
|
+
) -> Dict[str, Any]:
|
3545
|
+
"""
|
3546
|
+
Get average traded volume for a stock over a specified period.
|
3547
|
+
|
3548
|
+
Args:
|
3549
|
+
symbol (str): Stock symbol (e.g., HDFCBANK, NSE:RELIANCE).
|
3550
|
+
start_date (str): Period start date (YYYY-MM-DD).
|
3551
|
+
end_date (str): Period end date (YYYY-MM-DD).
|
3552
|
+
interval (str, optional): Time interval ('daily', 'weekly', 'monthly'). Defaults to 'daily'.
|
3553
|
+
|
3554
|
+
Returns:
|
3555
|
+
Dict[str, Any]: Average volume data.
|
3556
|
+
|
3557
|
+
Example Response:
|
3558
|
+
{
|
3559
|
+
"symbol": "HDFCBANK",
|
3560
|
+
"startDate": "2024-04-01",
|
3561
|
+
"endDate": "2024-06-30",
|
3562
|
+
"interval": "daily",
|
3563
|
+
"averageVolume": 1234567,
|
3564
|
+
"totalDays": 61,
|
3565
|
+
"unit": "shares"
|
3566
|
+
}
|
3567
|
+
"""
|
3568
|
+
params = self._normalize_params({
|
3569
|
+
"symbol": symbol,
|
3570
|
+
"startDate": start_date,
|
3571
|
+
"endDate": end_date,
|
3572
|
+
"interval": interval
|
3573
|
+
})
|
3574
|
+
|
3575
|
+
logger.debug("Fetching average traded volume for %s from %s to %s", symbol, start_date, end_date)
|
3576
|
+
return self._make_request("GET", self._routes["analytics.marketdata.average_volume"], params=params)
|
3577
|
+
|
3578
|
+
def get_index_max_drawdown(
|
3579
|
+
self,
|
3580
|
+
index_symbol: str,
|
3581
|
+
start_date: str,
|
3582
|
+
end_date: str,
|
3583
|
+
interval: str = "daily"
|
3584
|
+
) -> Dict[str, Any]:
|
3585
|
+
"""
|
3586
|
+
Get maximum drawdown for an index over a specified period.
|
3587
|
+
|
3588
|
+
Args:
|
3589
|
+
index_symbol (str): Index symbol (e.g., NIFTY50, BANKNIFTY, SENSEX).
|
3590
|
+
start_date (str): Period start date (YYYY-MM-DD).
|
3591
|
+
end_date (str): Period end date (YYYY-MM-DD).
|
3592
|
+
interval (str, optional): Time interval ('daily', 'weekly', 'monthly'). Defaults to 'daily'.
|
3593
|
+
|
3594
|
+
Returns:
|
3595
|
+
Dict[str, Any]: Maximum drawdown data.
|
3596
|
+
|
3597
|
+
Example Response:
|
3598
|
+
{
|
3599
|
+
"indexSymbol": "NIFTY50",
|
3600
|
+
"startDate": "2023-01-01",
|
3601
|
+
"endDate": "2024-01-01",
|
3602
|
+
"interval": "daily",
|
3603
|
+
"maxDrawdown": -15.23,
|
3604
|
+
"drawdownDate": "2023-10-26",
|
3605
|
+
"peakDate": "2023-09-19",
|
3606
|
+
"unit": "%"
|
3607
|
+
}
|
3608
|
+
"""
|
3609
|
+
params = self._normalize_params({
|
3610
|
+
"indexSymbol": index_symbol,
|
3611
|
+
"startDate": start_date,
|
3612
|
+
"endDate": end_date,
|
3613
|
+
"interval": interval
|
3614
|
+
})
|
3615
|
+
|
3616
|
+
logger.debug("Fetching index max drawdown for %s from %s to %s", index_symbol, start_date, end_date)
|
3617
|
+
return self._make_request("GET", self._routes["analytics.index.max_drawdown"], params=params)
|
3618
|
+
|
3619
|
+
def get_drawdown_duration(
|
3620
|
+
self,
|
3621
|
+
symbol: str,
|
3622
|
+
start_date: str,
|
3623
|
+
end_date: str,
|
3624
|
+
interval: str = "daily"
|
3625
|
+
) -> Dict[str, Any]:
|
3626
|
+
"""
|
3627
|
+
Get drawdown duration analysis for a stock or index.
|
3628
|
+
|
3629
|
+
Args:
|
3630
|
+
symbol (str): Stock or index symbol (e.g., INFY, NIFTY50).
|
3631
|
+
start_date (str): Start of analysis period (YYYY-MM-DD).
|
3632
|
+
end_date (str): End of analysis period (YYYY-MM-DD).
|
3633
|
+
interval (str, optional): Time interval ('daily', 'weekly', 'monthly'). Defaults to 'daily'.
|
3634
|
+
|
3635
|
+
Returns:
|
3636
|
+
Dict[str, Any]: Drawdown duration data.
|
3637
|
+
|
3638
|
+
Example Response:
|
3639
|
+
{
|
3640
|
+
"symbol": "INFY",
|
3641
|
+
"startDate": "2022-01-01",
|
3642
|
+
"endDate": "2024-01-01",
|
3643
|
+
"interval": "daily",
|
3644
|
+
"maxDrawdownDuration": 147,
|
3645
|
+
"averageDrawdownDuration": 23.5,
|
3646
|
+
"totalDrawdowns": 12,
|
3647
|
+
"unit": "days"
|
3648
|
+
}
|
3649
|
+
"""
|
3650
|
+
params = self._normalize_params({
|
3651
|
+
"symbol": symbol,
|
3652
|
+
"startDate": start_date,
|
3653
|
+
"endDate": end_date,
|
3654
|
+
"interval": interval
|
3655
|
+
})
|
3656
|
+
|
3657
|
+
logger.debug("Fetching drawdown duration for %s from %s to %s", symbol, start_date, end_date)
|
3658
|
+
return self._make_request("GET", self._routes["analytics.instrument.drawdown_duration"], params=params)
|
3659
|
+
|
3660
|
+
def get_rolling_peak_price(
|
3661
|
+
self,
|
3662
|
+
symbol: str,
|
3663
|
+
start_date: str,
|
3664
|
+
end_date: str,
|
3665
|
+
window: int,
|
3666
|
+
adjusted: bool = True,
|
3667
|
+
interval: str = "daily"
|
3668
|
+
) -> Dict[str, Any]:
|
3669
|
+
"""
|
3670
|
+
Get rolling peak price analysis for a stock.
|
3671
|
+
|
3672
|
+
Args:
|
3673
|
+
symbol (str): Stock symbol (e.g., RELIANCE, NSE:TCS).
|
3674
|
+
start_date (str): Start of evaluation period (YYYY-MM-DD).
|
3675
|
+
end_date (str): End of evaluation period (YYYY-MM-DD).
|
3676
|
+
window (int): Rolling window size in days (1-252).
|
3677
|
+
adjusted (bool, optional): Adjust for corporate actions. Defaults to True.
|
3678
|
+
interval (str, optional): Time interval ('daily', 'weekly', 'monthly'). Defaults to 'daily'.
|
3679
|
+
|
3680
|
+
Returns:
|
3681
|
+
Dict[str, Any]: Rolling peak price data.
|
3682
|
+
|
3683
|
+
Example Response:
|
3684
|
+
{
|
3685
|
+
"symbol": "NSE:TCS",
|
3686
|
+
"startDate": "2024-01-01",
|
3687
|
+
"endDate": "2024-06-30",
|
3688
|
+
"window": 20,
|
3689
|
+
"interval": "daily",
|
3690
|
+
"adjusted": true,
|
3691
|
+
"data": [
|
3692
|
+
{"date": "2024-01-01", "price": 3500.0, "rollingPeak": 3500.0},
|
3693
|
+
{"date": "2024-01-02", "price": 3520.0, "rollingPeak": 3520.0}
|
3694
|
+
]
|
3695
|
+
}
|
3696
|
+
"""
|
3697
|
+
params = self._normalize_params({
|
3698
|
+
"symbol": symbol,
|
3699
|
+
"startDate": start_date,
|
3700
|
+
"endDate": end_date,
|
3701
|
+
"window": window,
|
3702
|
+
"adjusted": adjusted,
|
3703
|
+
"interval": interval
|
3704
|
+
})
|
3705
|
+
|
3706
|
+
logger.debug("Fetching rolling peak price for %s from %s to %s with window %d", symbol, start_date, end_date, window)
|
3707
|
+
return self._make_request("GET", self._routes["analytics.price.rolling_peak"], params=params)
|
3708
|
+
|
3709
|
+
def get_rolling_price_mean(
|
3710
|
+
self,
|
3711
|
+
symbol: str,
|
3712
|
+
start_date: str,
|
3713
|
+
end_date: str,
|
3714
|
+
window: int,
|
3715
|
+
adjusted: bool = True,
|
3716
|
+
interval: str = "daily"
|
3717
|
+
) -> Dict[str, Any]:
|
3718
|
+
"""
|
3719
|
+
Get rolling mean price analysis for a stock.
|
3720
|
+
|
3721
|
+
Args:
|
3722
|
+
symbol (str): Stock symbol (e.g., HDFCBANK, NSE:WIPRO).
|
3723
|
+
start_date (str): Start of evaluation period (YYYY-MM-DD).
|
3724
|
+
end_date (str): End of evaluation period (YYYY-MM-DD).
|
3725
|
+
window (int): Rolling window size in days (1-252).
|
3726
|
+
adjusted (bool, optional): Adjust for corporate actions. Defaults to True.
|
3727
|
+
interval (str, optional): Time interval ('daily', 'weekly', 'monthly'). Defaults to 'daily'.
|
3728
|
+
|
3729
|
+
Returns:
|
3730
|
+
Dict[str, Any]: Rolling mean price data.
|
3731
|
+
|
3732
|
+
Example Response:
|
3733
|
+
{
|
3734
|
+
"symbol": "NSE:HDFCBANK",
|
3735
|
+
"startDate": "2024-01-01",
|
3736
|
+
"endDate": "2024-06-30",
|
3737
|
+
"window": 20,
|
3738
|
+
"interval": "daily",
|
3739
|
+
"adjusted": true,
|
3740
|
+
"data": [
|
3741
|
+
{"date": "2024-01-01", "price": 1500.0, "rollingMean": 1500.0},
|
3742
|
+
{"date": "2024-01-02", "price": 1510.0, "rollingMean": 1505.0}
|
3743
|
+
]
|
3744
|
+
}
|
3745
|
+
"""
|
3746
|
+
params = self._normalize_params({
|
3747
|
+
"symbol": symbol,
|
3748
|
+
"startDate": start_date,
|
3749
|
+
"endDate": end_date,
|
3750
|
+
"window": window,
|
3751
|
+
"adjusted": adjusted,
|
3752
|
+
"interval": interval
|
3753
|
+
})
|
3754
|
+
|
3755
|
+
logger.debug("Fetching rolling mean price for %s from %s to %s with window %d", symbol, start_date, end_date, window)
|
3756
|
+
return self._make_request("GET", self._routes["analytics.price.rolling_mean"], params=params)
|
3757
|
+
|
3758
|
+
def get_realized_volatility(
|
3759
|
+
self,
|
3760
|
+
symbol: str,
|
3761
|
+
start_date: str,
|
3762
|
+
end_date: str,
|
3763
|
+
adjusted: bool = True,
|
3764
|
+
interval: str = "daily"
|
3765
|
+
) -> Dict[str, Any]:
|
3766
|
+
"""
|
3767
|
+
Get realized price volatility for a stock.
|
3768
|
+
|
3769
|
+
Args:
|
3770
|
+
symbol (str): Stock symbol or instrument ID (e.g., NSE:ITC, BHARTIARTL).
|
3771
|
+
start_date (str): Start of volatility calculation window (YYYY-MM-DD).
|
3772
|
+
end_date (str): End of volatility calculation window (YYYY-MM-DD).
|
3773
|
+
adjusted (bool, optional): Adjust prices for corporate actions. Defaults to True.
|
3774
|
+
interval (str, optional): Time interval ('daily', 'weekly', 'monthly'). Defaults to 'daily'.
|
3775
|
+
|
3776
|
+
Returns:
|
3777
|
+
Dict[str, Any]: Realized volatility data.
|
3778
|
+
|
3779
|
+
Example Response:
|
3780
|
+
{
|
3781
|
+
"symbol": "NSE:ITC",
|
3782
|
+
"startDate": "2024-01-01",
|
3783
|
+
"endDate": "2024-06-30",
|
3784
|
+
"interval": "daily",
|
3785
|
+
"adjusted": true,
|
3786
|
+
"realizedVolatility": 22.45,
|
3787
|
+
"annualizedVolatility": 35.67,
|
3788
|
+
"unit": "%"
|
3789
|
+
}
|
3790
|
+
"""
|
3791
|
+
params = self._normalize_params({
|
3792
|
+
"symbol": symbol,
|
3793
|
+
"startDate": start_date,
|
3794
|
+
"endDate": end_date,
|
3795
|
+
"adjusted": adjusted,
|
3796
|
+
"interval": interval
|
3797
|
+
})
|
3798
|
+
|
3799
|
+
logger.debug("Fetching realized volatility for %s from %s to %s", symbol, start_date, end_date)
|
3800
|
+
return self._make_request("GET", self._routes["analytics.volatility.realized"], params=params)
|
3801
|
+
|
3802
|
+
def get_beta_90d(
|
3803
|
+
self,
|
3804
|
+
symbol: str,
|
3805
|
+
benchmark: str = "NIFTY50"
|
3806
|
+
) -> Dict[str, Any]:
|
3807
|
+
"""
|
3808
|
+
Get 90-day CAPM Beta for a stock relative to a benchmark.
|
3809
|
+
|
3810
|
+
Args:
|
3811
|
+
symbol (str): Stock symbol (e.g., RELIANCE, ITC).
|
3812
|
+
benchmark (str, optional): Benchmark index. Defaults to 'NIFTY50'.
|
3813
|
+
|
3814
|
+
Returns:
|
3815
|
+
Dict[str, Any]: 90-day beta data.
|
3816
|
+
|
3817
|
+
Example Response:
|
3818
|
+
{
|
3819
|
+
"symbol": "ITC",
|
3820
|
+
"benchmark": "NIFTY50",
|
3821
|
+
"period": "90d",
|
3822
|
+
"beta": 0.85,
|
3823
|
+
"correlation": 0.72,
|
3824
|
+
"rSquared": 0.52,
|
3825
|
+
"alpha": 0.03,
|
3826
|
+
"unit": "ratio"
|
3827
|
+
}
|
3828
|
+
"""
|
3829
|
+
params = self._normalize_params({
|
3830
|
+
"symbol": symbol,
|
3831
|
+
"benchmark": benchmark
|
3832
|
+
})
|
3833
|
+
|
3834
|
+
logger.debug("Fetching 90d beta for %s vs %s", symbol, benchmark)
|
3835
|
+
return self._make_request("GET", self._routes["analytics.risk.beta_90d"], params=params)
|
3836
|
+
|
3837
|
+
def get_beta_custom_period(
|
3838
|
+
self,
|
3839
|
+
symbol: str,
|
3840
|
+
benchmark: str,
|
3841
|
+
start_date: str,
|
3842
|
+
end_date: str
|
3843
|
+
) -> Dict[str, Any]:
|
3844
|
+
"""
|
3845
|
+
Get CAPM Beta for a stock relative to a benchmark over a custom period.
|
3846
|
+
|
3847
|
+
Args:
|
3848
|
+
symbol (str): Stock symbol.
|
3849
|
+
benchmark (str): Benchmark index symbol.
|
3850
|
+
start_date (str): Start date (YYYY-MM-DD).
|
3851
|
+
end_date (str): End date (YYYY-MM-DD).
|
3852
|
+
|
3853
|
+
Returns:
|
3854
|
+
Dict[str, Any]: Custom period beta data.
|
3855
|
+
|
3856
|
+
Example Response:
|
3857
|
+
{
|
3858
|
+
"symbol": "ITC",
|
3859
|
+
"benchmark": "NIFTY50",
|
3860
|
+
"startDate": "2023-01-01",
|
3861
|
+
"endDate": "2025-01-01",
|
3862
|
+
"beta": 0.87,
|
3863
|
+
"correlation": 0.74,
|
3864
|
+
"rSquared": 0.55,
|
3865
|
+
"alpha": 0.02,
|
3866
|
+
"unit": "ratio"
|
3867
|
+
}
|
3868
|
+
"""
|
3869
|
+
params = self._normalize_params({
|
3870
|
+
"symbol": symbol,
|
3871
|
+
"benchmark": benchmark,
|
3872
|
+
"startDate": start_date,
|
3873
|
+
"endDate": end_date
|
3874
|
+
})
|
3875
|
+
|
3876
|
+
logger.debug("Fetching custom period beta for %s vs %s from %s to %s", symbol, benchmark, start_date, end_date)
|
3877
|
+
return self._make_request("GET", self._routes["analytics.risk.beta_custom"], params=params)
|