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/__init__.py +1 -1
- wiz_trader/apis/client.py +1274 -4
- {wiz_trader-0.36.0.dist-info → wiz_trader-0.38.0.dist-info}/METADATA +473 -39
- wiz_trader-0.38.0.dist-info/RECORD +9 -0
- wiz_trader-0.36.0.dist-info/RECORD +0 -9
- {wiz_trader-0.36.0.dist-info → wiz_trader-0.38.0.dist-info}/WHEEL +0 -0
- {wiz_trader-0.36.0.dist-info → wiz_trader-0.38.0.dist-info}/top_level.txt +0 -0
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,
|
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,
|
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=
|
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)
|