From 848ca0fbd58ac4615e29c83e738177eaeca6cbf1 Mon Sep 17 00:00:00 2001 From: gonzo Date: Sun, 8 Sep 2024 18:27:29 +0200 Subject: [PATCH 1/7] add last split to company information, issue #7 --- ib_fundamental/__init__.py | 2 +- ib_fundamental/fundamental.py | 2 +- ib_fundamental/objects.py | 2 ++ ib_fundamental/xml_parser.py | 10 ++++++++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ib_fundamental/__init__.py b/ib_fundamental/__init__.py index e945494..c119a8c 100644 --- a/ib_fundamental/__init__.py +++ b/ib_fundamental/__init__.py @@ -22,7 +22,7 @@ __copyright__ = "Copyright 2024 Gonzalo Sáenz" __credits__ = ["Gonzalo Sáenz"] __license__ = "Apache 2.0" -__version__ = "0.0.4" +__version__ = "0.0.5" __maintainer__ = "Gonzalo Sáenz" diff --git a/ib_fundamental/fundamental.py b/ib_fundamental/fundamental.py index ea75218..b3d3569 100644 --- a/ib_fundamental/fundamental.py +++ b/ib_fundamental/fundamental.py @@ -274,7 +274,7 @@ def company_info(self) -> CompanyInfo: try: return self.__company_info except AttributeError: - self.__company_info = self.parser.get_company_info() + self.__company_info: CompanyFinancials = self.parser.get_company_info() return self.__company_info diff --git a/ib_fundamental/objects.py b/ib_fundamental/objects.py index 4c0f9b3..c2a3e1d 100644 --- a/ib_fundamental/objects.py +++ b/ib_fundamental/objects.py @@ -342,6 +342,8 @@ class CompanyInfo: exchange_code: str exchange: str irs: str + last_split: datetime + stock_split: float @dataclass(slots=True) diff --git a/ib_fundamental/xml_parser.py b/ib_fundamental/xml_parser.py index 4d410c7..d6ae8ff 100644 --- a/ib_fundamental/xml_parser.py +++ b/ib_fundamental/xml_parser.py @@ -396,6 +396,14 @@ def get_company_info(self) -> CompanyInfo: exchange_code = { "code": r.attrib["Code"] for r in fs.findall("./Issues/Issue/Exchange") } + last_split = { + "last_split": fromisoformat(r.attrib.get("Date")) + for r in fs.findall("./Issues/Issue/MostRecentSplit") + } + stock_split = { + "stock_split": float(r.text) + for r in fs.findall("./Issues/Issue/MostRecentSplit") + } _company_info = CompanyInfo( ticker=issue_id.get("Ticker"), company_name=coids.get("CompanyName"), @@ -403,5 +411,7 @@ def get_company_info(self) -> CompanyInfo: exchange_code=exchange_code.get("code"), exchange=exchange.get("Exchange"), irs=coids.get("IRSNo"), + last_split=last_split.get("last_split"), + stock_split=stock_split.get("stock_split"), ) return _company_info From 28a86adb8bd26cf64bcca316cfe9f9a1d564e423 Mon Sep 17 00:00:00 2001 From: gonzo Date: Sun, 8 Sep 2024 23:27:56 +0200 Subject: [PATCH 2/7] Fixes for: - ownership report - ratios - financial statements --- ib_fundamental/fundamental.py | 128 ++++++++++++---------------------- ib_fundamental/objects.py | 50 ++++++++----- ib_fundamental/utils.py | 46 +++++++++++- ib_fundamental/xml_parser.py | 8 ++- 4 files changed, 128 insertions(+), 104 deletions(-) diff --git a/ib_fundamental/fundamental.py b/ib_fundamental/fundamental.py index b3d3569..d1813ed 100644 --- a/ib_fundamental/fundamental.py +++ b/ib_fundamental/fundamental.py @@ -28,7 +28,6 @@ from datetime import datetime from typing import Optional -import pandas as pd from ib_async import IB, Dividends, FundamentalRatios, Stock, Ticker from pandas import DataFrame @@ -45,11 +44,8 @@ OwnershipReport, RatioSnapshot, Revenue, - StatementCode, - StatementData, - statement_type, ) -from ib_fundamental.utils import to_dataframe +from ib_fundamental.utils import build_statement, to_dataframe from .ib_client import IBClient from .xml_parser import XMLParser @@ -94,7 +90,7 @@ def income_annual(self) -> IncomeSet: try: return self.__income_annual except AttributeError: - self.__income_annual = self.parser.get_fin_statement( + self.__income_annual: IncomeSet = self.parser.get_fin_statement( statement="INC", period="annual" ) return self.__income_annual @@ -105,7 +101,7 @@ def income_quarter(self) -> IncomeSet: try: return self.__income_quarter except AttributeError: - self.__income_quarter = self.parser.get_fin_statement( + self.__income_quarter: IncomeSet = self.parser.get_fin_statement( statement="INC", period="quarter" ) return self.__income_quarter @@ -115,7 +111,7 @@ def balance_annual(self) -> BalanceSheetSet: try: return self.__balance_annual except AttributeError: - self.__balance_annual = self.parser.get_fin_statement( + self.__balance_annual: BalanceSheetSet = self.parser.get_fin_statement( statement="BAL", period="annual" ) return self.__balance_annual @@ -125,7 +121,7 @@ def balance_quarter(self) -> BalanceSheetSet: try: return self.__balance_quarter except AttributeError: - self.__balance_quarter = self.parser.get_fin_statement( + self.__balance_quarter: BalanceSheetSet = self.parser.get_fin_statement( statement="BAL", period="quarter" ) return self.__balance_quarter @@ -135,7 +131,7 @@ def cashflow_annual(self) -> CashFlowSet: try: return self.__cashflow_annual except AttributeError: - self.__cashflow_annual = self.parser.get_fin_statement( + self.__cashflow_annual: CashFlowSet = self.parser.get_fin_statement( statement="CAS", period="annual" ) return self.__cashflow_annual @@ -145,7 +141,7 @@ def cashflow_quarter(self) -> CashFlowSet: try: return self.__cashflow_quarter except AttributeError: - self.__cashflow_quarter = self.parser.get_fin_statement( + self.__cashflow_quarter: CashFlowSet = self.parser.get_fin_statement( statement="CAS", period="quarter" ) return self.__cashflow_quarter @@ -156,7 +152,9 @@ def ownership_report(self) -> OwnershipReport: try: return self.__ownership_report except AttributeError: - self.__ownership_report = self.parser.get_ownership_report() + self.__ownership_report: OwnershipReport = ( + self.parser.get_ownership_report() + ) return self.__ownership_report @property @@ -192,7 +190,9 @@ def revenue_ttm(self) -> list[Revenue]: try: return self.__revenue_ttm except AttributeError: - self.__revenue_ttm = self.parser.get_revenue(report_type="TTM") + self.__revenue_ttm: list[Revenue] = self.parser.get_revenue( + report_type="TTM" + ) return self.__revenue_ttm @property @@ -200,7 +200,9 @@ def revenue_q(self) -> list[Revenue]: try: return self.__revenue_q except AttributeError: - self.__revenue_q = self.parser.get_revenue(report_type="R", period="3M") + self.__revenue_q: list[Revenue] = self.parser.get_revenue( + report_type="R", period="3M" + ) return self.__revenue_q @property @@ -208,7 +210,9 @@ def eps_ttm(self) -> list[EarningsPerShare]: try: return self.__eps_ttm except AttributeError: - self.__eps_ttm = self.parser.get_eps(report_type="TTM") + self.__eps_ttm: list[EarningsPerShare] = self.parser.get_eps( + report_type="TTM" + ) return self.__eps_ttm @property @@ -216,7 +220,9 @@ def eps_q(self) -> list[EarningsPerShare]: try: return self.__eps_q except AttributeError: - self.__eps_q = self.parser.get_eps(report_type="R", period="3M") + self.__eps_q: list[EarningsPerShare] = self.parser.get_eps( + report_type="R", period="3M" + ) return self.__eps_q @property @@ -224,7 +230,9 @@ def analyst_forecast(self) -> AnalystForecast: try: return self.__analyst_forecast except AttributeError: - self.__analyst_forecast = self.parser.get_analyst_forecast() + self.__analyst_forecast: AnalystForecast = ( + self.parser.get_analyst_forecast() + ) return self.__analyst_forecast @property @@ -232,7 +240,7 @@ def ratios(self) -> RatioSnapshot: try: return self.__ratios except AttributeError: - self.__ratios = self.parser.get_ratios() + self.__ratios: RatioSnapshot = self.parser.get_ratios() return self.__ratios @property @@ -240,7 +248,7 @@ def fundamental_ratios(self) -> FundamentalRatios: try: return self.__fundamental_ratios except AttributeError: - self.__fundamental_ratios = self.client.get_ratios() + self.__fundamental_ratios: FundamentalRatios = self.client.get_ratios() self.ticker = self.client.ib.ticker(self.contract) return self.__fundamental_ratios @@ -249,7 +257,7 @@ def dividend_summary(self) -> Dividends: try: return self.__dividend_summary except AttributeError: - self.__dividend_summary = self.client.get_dividends() + self.__dividend_summary: Dividends = self.client.get_dividends() self.ticker = self.client.ib.ticker(self.contract) return self.__dividend_summary @@ -258,7 +266,7 @@ def fy_estimates(self) -> list[ForwardYear]: try: return self.__fy_estimates except AttributeError: - self.__fy_estimates = self.parser.get_fy_estimates() + self.__fy_estimates: list[ForwardYear] = self.parser.get_fy_estimates() return self.__fy_estimates @property @@ -266,7 +274,7 @@ def fy_actuals(self) -> list[ForwardYear]: try: return self.__fy_actuals except AttributeError: - self.__fy_actuals = self.parser.get_fy_actuals() + self.__fy_actuals: list[ForwardYear] = self.parser.get_fy_actuals() return self.__fy_actuals @property @@ -274,7 +282,7 @@ def company_info(self) -> CompanyInfo: try: return self.__company_info except AttributeError: - self.__company_info: CompanyFinancials = self.parser.get_company_info() + self.__company_info: CompanyInfo = self.parser.get_company_info() return self.__company_info @@ -299,95 +307,47 @@ def __repr__(self): cls_name = self.__class__.__qualname__ return f"{cls_name}(symbol={self.data.symbol!r},IB={self.data.client.ib!r})" - def _get_data_frame( - self, - statement: StatementData, - ) -> DataFrame: - """Build dataframe for pp""" - _df = to_dataframe(statement) - return _df.T.sort_index(axis=1, ascending=False) # sort columns - - def _get_map_items(self, stat_code: StatementCode) -> DataFrame: - """build map items for pp""" - _df = to_dataframe(self.data.parser.get_map_items(statement=stat_code)) - _df.coa_item = _df.coa_item.str.lower() - return _df - - def _get_header( - self, data: DataFrame, statement_code: StatementCode, idx: int = 6 - ) -> DataFrame: - """build header for pp""" - _header = data.iloc[:idx] - _header = ( - _header.assign(line_id=range(idx)) - .assign(statement_type=statement_code) - .reset_index() - .rename(columns={"index": "map_item"}) - ) - return _header.assign(coa_item=_header["map_item"]) - - def _join( - self, data: DataFrame, header: DataFrame, mapping: DataFrame, idx: int - ) -> DataFrame: - """join data to present""" - _pp = mapping.join(data, on="coa_item") - _df = pd.concat([header, _pp]).set_index("line_id") - - (_names,) = _df.loc[ - _df["coa_item"] == "end_date", _df.columns[1:idx] - ].values.tolist() - _l = _df.columns.to_list() - _l[1:idx] = _names - _df.columns = _l - _df.statement_type = _df.statement_type.map(lambda x: statement_type[x]) - _df = _df.drop(columns="coa_item").dropna() - return _df - - def _build_statement( - self, data: StatementData, statement_code: StatementCode, idx: int - ) -> DataFrame: - """build statement pp""" - _map = self._get_map_items(stat_code=statement_code) - _data = self._get_data_frame(statement=data) - _header = self._get_header(data=_data, statement_code=statement_code) - # pp - return self._join(data=_data, header=_header, mapping=_map, idx=idx) - @property def balance_quarter(self) -> DataFrame | None: """Quarterly balance statement""" if self.data.balance_quarter: - return self._build_statement(self.data.balance_quarter, "BAL", 6) + mapping = self.data.parser.get_map_items("BAL") + return build_statement(self.data.balance_quarter, "BAL", mapping) return None @property def balance_annual(self) -> DataFrame | None: if self.data.balance_annual: - return self._build_statement(self.data.balance_annual, "BAL", 7) + mapping = self.data.parser.get_map_items("BAL") + return build_statement(self.data.balance_annual, "BAL", mapping) return None @property def income_quarter(self) -> DataFrame | None: if self.data.income_quarter: - return self._build_statement(self.data.income_quarter, "INC", 6) + mapping = self.data.parser.get_map_items("INC") + return build_statement(self.data.income_quarter, "INC", mapping) return None @property def income_annual(self) -> DataFrame | None: if self.data.income_annual: - return self._build_statement(self.data.income_annual, "INC", 7) + mapping = self.data.parser.get_map_items("INC") + return build_statement(self.data.income_annual, "INC", mapping) return None @property def cashflow_quarter(self) -> DataFrame | None: if self.data.cashflow_annual: - return self._build_statement(self.data.cashflow_quarter, "CAS", 6) + mapping = self.data.parser.get_map_items("CAS") + return build_statement(self.data.cashflow_quarter, "CAS", mapping) return None @property def cashflow_annual(self) -> DataFrame | None: if self.data.cashflow_annual: - return self._build_statement(self.data.cashflow_annual, "CAS", 7) + mapping = self.data.parser.get_map_items("CAS") + return build_statement(self.data.cashflow_annual, "CAS", mapping) return None @property @@ -465,7 +425,7 @@ def company_information(self) -> DataFrame | None: @property def ratios(self) -> DataFrame | None: if self.data.ratios: - return to_dataframe([self.data.ratios]).T + return to_dataframe([self.data.ratios]).T.dropna() return None @property diff --git a/ib_fundamental/objects.py b/ib_fundamental/objects.py index c2a3e1d..1928628 100644 --- a/ib_fundamental/objects.py +++ b/ib_fundamental/objects.py @@ -299,21 +299,35 @@ class RatioSnapshot: vol10davg: float ev: float mktcap: float - ttmrev: float - ttmebitd: float - ttmniac: float - ttmepsxclx: float - ttmrevps: float - qbvps: float - qcshps: float - ttmcfshr: float - ttmdivshr: float - ttmgrosmgn: float - ttmroepct: float - ttmpr2rev: float - peexclxor: float - price2bk: float - employees: float + ttmrev: Optional[float] = None + ttmebitd: Optional[float] = None + ttmniac: Optional[float] = None + ttmepsxclx: Optional[float] = None + ttmrevps: Optional[float] = None + qbvps: Optional[float] = None + qcshps: Optional[float] = None + ttmcfshr: Optional[float] = None + ttmdivshr: Optional[float] = None + ttmgrosmgn: Optional[float] = None + ttmroepct: Optional[float] = None + ttmpr2rev: Optional[float] = None + peexclxor: Optional[float] = None + price2bk: Optional[float] = None + arev: Optional[float] = None + aebitd: Optional[float] = None + aniac: Optional[float] = None + aepsxclxor: Optional[float] = None + arevps: Optional[float] = None + abvps: Optional[float] = None + acshps: Optional[float] = None + acfshr: Optional[float] = None + adivshr: Optional[float] = None + agrosmgn: Optional[float] = None + aroepct: Optional[float] = None + apr2rev: Optional[float] = None + apeexclxor: Optional[float] = None + aprice2bk: Optional[float] = None + employees: Optional[float] = None @dataclass(slots=True) @@ -352,7 +366,7 @@ class OwnershipCompany: ISIN: str # pylint: disable=invalid-name float_shares: int - as_of_date: datetime + as_of_date: datetime | None @dataclass(slots=True) @@ -371,5 +385,5 @@ class OwnershipDetails: class OwnershipReport: """Ownership Report""" - company: OwnershipCompany - ownership_details: list[OwnershipDetails] + company: OwnershipCompany | None = None + ownership_details: list[OwnershipDetails] | None = None diff --git a/ib_fundamental/utils.py b/ib_fundamental/utils.py index 5aef701..b331ad4 100644 --- a/ib_fundamental/utils.py +++ b/ib_fundamental/utils.py @@ -27,7 +27,9 @@ from typing import Any, Optional from ib_async import FundamentalRatios -from pandas import DataFrame +from pandas import DataFrame, Index, concat + +from .objects import StatementCode, StatementData, StatementMapping, statement_type re_pattern = re.compile(r"(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])") @@ -69,3 +71,45 @@ def default(self, o): return super().default(o) return json.dumps(obj, cls=EnhancedJSONEncoder, **kwargs) + + +def get_df_header(data: DataFrame, statement_code: StatementCode) -> DataFrame: + """build header for pp""" + idx: int = 6 + _header = data.iloc[:idx] + _header = ( + _header.assign(line_id=range(idx)) + .assign(statement_type=statement_code) + .reset_index() + .rename(columns={"index": "map_item"}) + ) + return _header.assign(coa_item=_header["map_item"]) + + +def join_df(data: DataFrame, header: DataFrame, mapping: DataFrame) -> DataFrame: + """join data to present""" + _pp = mapping.join(data, on="coa_item") + _df = concat([header, _pp]).set_index("line_id") + + _names = _df.loc[ + _df.loc[:, "coa_item"] == "end_date", _df.columns[1:-2] + ].values.tolist()[0] + _l = _df.columns.to_list() + _l[1:-2] = _names + _df.columns = Index(_l) + _df["statement_type"] = _df["statement_type"].map(lambda x: statement_type[x]) + _df = _df.drop(columns="coa_item").dropna() + return _df + + +def build_statement( + data: StatementData, statement_code: StatementCode, mapping: StatementMapping +) -> DataFrame: + """build statement pp""" + _map = to_dataframe(mapping) + _map.coa_item = _map.coa_item.str.lower() + # + _data = to_dataframe(data).T.sort_index(axis=1, ascending=False) # sort columns + _header = get_df_header(data=_data, statement_code=statement_code) + # pp + return join_df(data=_data, header=_header, mapping=_map) diff --git a/ib_fundamental/xml_parser.py b/ib_fundamental/xml_parser.py index d6ae8ff..568dedc 100644 --- a/ib_fundamental/xml_parser.py +++ b/ib_fundamental/xml_parser.py @@ -26,6 +26,7 @@ ] from datetime import date, datetime +from functools import lru_cache from typing import Literal, Optional import pandas as pd @@ -151,6 +152,7 @@ def get_fin_statement( return [statement_map[statement](**i, **j) for i, j in zip(fperiods, fs)] + @lru_cache(maxsize=4) def get_map_items( self, statement: Optional[StatementCode] = None ) -> StatementMapping: @@ -186,7 +188,11 @@ def get_ownership_report(self) -> OwnershipReport: company = OwnershipCompany( ISIN=isin.text, float_shares=int(fa.text), - as_of_date=fromisoformat(fa.attrib["asofDate"]), + as_of_date=( + fromisoformat(fa.attrib["asofDate"]) + if fa.attrib["asofDate"] != "0" + else None + ), ) _l = [] fa = fs.findall("./Owner") From d030d2ac2c6a328361c27f73955eab7a1f3cafd5 Mon Sep 17 00:00:00 2001 From: gonzo Date: Sun, 8 Sep 2024 23:37:11 +0200 Subject: [PATCH 3/7] update build and test workflow --- .github/workflows/build-n-test.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/build-n-test.yml b/.github/workflows/build-n-test.yml index 9cb194d..0716351 100644 --- a/.github/workflows/build-n-test.yml +++ b/.github/workflows/build-n-test.yml @@ -31,18 +31,10 @@ jobs: run: python -c "import sys; print(sys.version)" - name: Install dependencies run: | - python -m pip install --upgrade pip setuptools wheel .[dev] + python -m pip install --user -U build pip setuptools wheel .[dev] - name: Lint with pylint run: | pylint -rn --rcfile=pyproject.toml ${{ env.package }} continue-on-error: true - - name: Install pypa/build - run: >- - python3 -m pip install build --user - name: Build a binary wheel and a source tarball run: python3 -m build - # - name: Test with pytest - # run: | - # pip install pytest pytest-cov - # pytest tests --doctest-modules --junitxml=junit/test-results.xml \ - # --cov=com --cov-report=xml --cov-report=html From 4e6be45c36c6f2935ce1a1c30f7145dfe629be3b Mon Sep 17 00:00:00 2001 From: gonzo Date: Mon, 9 Sep 2024 13:02:14 +0200 Subject: [PATCH 4/7] refactor dividends --- ib_fundamental/objects.py | 12 ++++++------ ib_fundamental/xml_parser.py | 22 +++++++++++++++++----- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/ib_fundamental/objects.py b/ib_fundamental/objects.py index 1928628..f66aa60 100644 --- a/ib_fundamental/objects.py +++ b/ib_fundamental/objects.py @@ -230,13 +230,13 @@ class StatementMap: class Dividend: """Dividend""" - type: str - ex_date: datetime - record_date: datetime - pay_date: datetime - declaration_date: datetime + type: str | None + ex_date: datetime | None + record_date: datetime | None + pay_date: datetime | None + declaration_date: datetime | None currency: str - value: float + value: float | None @dataclass(slots=True) diff --git a/ib_fundamental/xml_parser.py b/ib_fundamental/xml_parser.py index 568dedc..08a3df9 100644 --- a/ib_fundamental/xml_parser.py +++ b/ib_fundamental/xml_parser.py @@ -220,12 +220,24 @@ def get_dividend(self) -> list[Dividend] | None: _dividend = [ Dividend( type=i.attrib["type"], - ex_date=fromisoformat(i.attrib["exDate"]), - record_date=fromisoformat(i.attrib["recordDate"]), - pay_date=fromisoformat(i.attrib["payDate"]), - declaration_date=fromisoformat(i.attrib["declarationDate"]), + ex_date=( + fromisoformat(i.attrib["exDate"]) if i.attrib["exDate"] else None + ), + record_date=( + fromisoformat(i.attrib["recordDate"]) + if i.attrib["recordDate"] + else None + ), + pay_date=( + fromisoformat(i.attrib["payDate"]) if i.attrib["payDate"] else None + ), + declaration_date=( + fromisoformat(i.attrib["declarationDate"]) + if i.attrib["declarationDate"] + else None + ), currency=curr, - value=float(i.text), + value=float(i.text) if i.text else None, ) for i in fs ] From 96816544570622b33884e9752a29d913e176af4e Mon Sep 17 00:00:00 2001 From: gonzo Date: Mon, 9 Sep 2024 13:23:34 +0200 Subject: [PATCH 5/7] fix 'Requested market data is not subscribed' on fundamental_ratios --- ib_fundamental/ib_client.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ib_fundamental/ib_client.py b/ib_fundamental/ib_client.py index ef81426..d9505bd 100644 --- a/ib_fundamental/ib_client.py +++ b/ib_fundamental/ib_client.py @@ -126,19 +126,21 @@ def get_ticker(self) -> Ticker: ) return self.ticker - def get_ratios(self) -> FundamentalRatios: + def get_ratios(self) -> FundamentalRatios | None: """request market data ticker with fundamental ratios""" self.get_ticker() if self.ticker.fundamentalRatios is None: - while self.ticker.fundamentalRatios is None: + for _ in self.ib.loopUntil( + self.ticker.fundamentalRatios is not None, timeout=2 + ): self.ib.sleep(0.0) return self.ticker.fundamentalRatios - def get_dividends(self) -> Dividends: + def get_dividends(self) -> Dividends | None: """get dividend information from ticker""" self.get_ticker() if self.ticker.dividends is None: - while self.ticker.dividends is None: + for _ in self.ib.loopUntil(self.ticker.dividends is not None, timeout=2): self.ib.sleep(0.0) return self.ticker.dividends From bf8d03dc8f2c17ce2cafa461db20853ffb72ca65 Mon Sep 17 00:00:00 2001 From: gonzo Date: Mon, 9 Sep 2024 14:38:58 +0200 Subject: [PATCH 6/7] disable pylint warnings on tests --- tests/fundamental_data_test.py | 2 ++ tests/utils_test.py | 1 + tests/xml_parser_test.py | 2 ++ 3 files changed, 5 insertions(+) diff --git a/tests/fundamental_data_test.py b/tests/fundamental_data_test.py index 58dbc1c..f175c40 100644 --- a/tests/fundamental_data_test.py +++ b/tests/fundamental_data_test.py @@ -35,6 +35,8 @@ class TestFundamentalData: """Test FundamentalData class""" + # pylint: disable=duplicate-code + def test_statments(self, fundamental_statement): """Test FundamentalData statements, income, cashflow, balance sheet by period, Annual and Interim diff --git a/tests/utils_test.py b/tests/utils_test.py index 89de6f4..10daa20 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -36,6 +36,7 @@ def fund_method(fundamental_data, request): yield fundamental_data, _m +# pylint: disable=too-few-public-methods class TestUtils: """Tests for utils module""" diff --git a/tests/xml_parser_test.py b/tests/xml_parser_test.py index f4946f8..7400cab 100644 --- a/tests/xml_parser_test.py +++ b/tests/xml_parser_test.py @@ -36,6 +36,8 @@ class TestXMLParser: """Test XMLParser class""" + # pylint: disable=duplicate-code + def test_statement(self, xml_parser_statement): """Test XML Parser statements""" _parser_statement, _statement, _period = xml_parser_statement From be72c5ac91e230fef12a020c5ea101a77d976bdf Mon Sep 17 00:00:00 2001 From: gonzo Date: Mon, 9 Sep 2024 14:47:42 +0200 Subject: [PATCH 7/7] updates typing --- ib_fundamental/fundamental.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ib_fundamental/fundamental.py b/ib_fundamental/fundamental.py index d1813ed..e8e3c34 100644 --- a/ib_fundamental/fundamental.py +++ b/ib_fundamental/fundamental.py @@ -244,20 +244,22 @@ def ratios(self) -> RatioSnapshot: return self.__ratios @property - def fundamental_ratios(self) -> FundamentalRatios: + def fundamental_ratios(self) -> FundamentalRatios | None: try: return self.__fundamental_ratios except AttributeError: - self.__fundamental_ratios: FundamentalRatios = self.client.get_ratios() + self.__fundamental_ratios: FundamentalRatios | None = ( + self.client.get_ratios() + ) self.ticker = self.client.ib.ticker(self.contract) return self.__fundamental_ratios @property - def dividend_summary(self) -> Dividends: + def dividend_summary(self) -> Dividends | None: try: return self.__dividend_summary except AttributeError: - self.__dividend_summary: Dividends = self.client.get_dividends() + self.__dividend_summary: Dividends | None = self.client.get_dividends() self.ticker = self.client.ib.ticker(self.contract) return self.__dividend_summary