Source code for nse_xbrl_parser.typed.shareholding

"""
nse_xbrl_parser.typed.shareholding

High-level typed model for Regulation 31 Shareholding Pattern (SHP) filings.
"""
from __future__ import annotations

from datetime import date
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field

from ..core.models import XBRLInstance


[docs] class ShareholderCategory(BaseModel): """Holding summary for a shareholder group (Promoter, Public, Non-Promoter).""" category_name: str number_of_shareholders: Optional[int] = None number_of_fully_paid_shares: Optional[int] = None total_shares: Optional[int] = None shareholding_percentage: Optional[float] = None voting_rights_percentage: Optional[float] = None shares_pledged_or_encumbered: Optional[int] = None pledge_percentage: Optional[float] = None
[docs] class ShareholdingPatternModel(BaseModel): """Normalized typed representation of Regulation 31 Shareholding Pattern.""" company_name: Optional[str] = None scrip_code: Optional[str] = None symbol: Optional[str] = None quarter_ended: Optional[date] = None declaration_quarter: Optional[str] = None # Summary Percentages promoter_holding_percentage: Optional[float] = None public_holding_percentage: Optional[float] = None non_promoter_non_public_percentage: Optional[float] = None promoter_shares_pledged_percentage: Optional[float] = None # Granular Breakdown categories: List[ShareholderCategory] = Field(default_factory=list) raw_facts_count: int = 0
[docs] @classmethod def from_instance(cls, instance: XBRLInstance) -> ShareholdingPatternModel: """Construct a strongly typed ShareholdingPatternModel from XBRLInstance.""" model = cls() model.company_name = instance.get_fact_value("NameOfTheCompany") model.scrip_code = ( str(instance.get_fact_value("ScripCode")) if instance.get_fact_value("ScripCode") is not None else instance.get_entity_id() ) model.symbol = instance.get_fact_value("NSESymbol") model.quarter_ended = instance.get_fact_value("QuarterEnded") or instance.get_fact_value("DateOfReport") model.declaration_quarter = instance.get_fact_value("ReportingQuarter") # Extract promoter & public percentages for fact in instance.facts: name_lower = fact.local_name.lower() if "promoterandpromotergroup" in name_lower and "percentage" in name_lower: if model.promoter_holding_percentage is None: model.promoter_holding_percentage = fact.as_float elif "publicshareholding" in name_lower and "percentage" in name_lower: if model.public_holding_percentage is None: model.public_holding_percentage = fact.as_float elif "pledged" in name_lower and "percentage" in name_lower: if model.promoter_shares_pledged_percentage is None: model.promoter_shares_pledged_percentage = fact.as_float model.raw_facts_count = len(instance.facts) return model