Skip to content

Commit

Permalink
Merge branch 'master' of github.com:quantbelt/ib_fundamental
Browse files Browse the repository at this point in the history
  • Loading branch information
gnzsnz committed May 29, 2024
2 parents ca4a2fc + 1ccb083 commit 5405b68
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 9 deletions.
66 changes: 62 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ exchange_code NASD
exchange NASDAQ
irs 942404110

# Income statement, aapl.income_quarter will pull the quarterly report
# Annual income statement, while aapl.income_quarter will pull the quarterly report
aapl.income_annual

map_item 2018-09-29 2019-09-28 2020-09-26 2021-09-25 2022-09-24 2023-09-30 statement_type
Expand Down Expand Up @@ -73,7 +73,7 @@ line_id
1770 Diluted Normalized EPS 3.05148 2.97145 3.27535 5.61402 6.1132 6.13405 Income Statement

# get earnings per share, appl.eps_ttm will pull trailing twelve months eps
aapl.eps_q
aapl.eps_ttm

report_type period eps
as_of_date
Expand Down Expand Up @@ -106,6 +106,14 @@ as_of_date
2023-12-31 TTM 12M 6.460
2024-03-31 TTM 12M 6.460

# get data in json format

from ib_fundamental.utils import to_json

# CompanyFinancials.data property contains all data in dataclass format
to_json(aapl.data.eps_ttm)
'[{"as_of_date": "2024-03-31T00:00:00", "report_type": "TTM", "period": "12M", "eps": 6.46}, {"as_of_date": "2023-12-31T00:00:00", "report_type": "TTM", "period": "12M", "eps": 6.46}, ...'

# and much more
```

Expand Down Expand Up @@ -156,8 +164,9 @@ This is the full list of methods of `CompanyFinancials` class
- revenue_q
- revenue_tt

You can use `FundamentalData` class that will return company
fundamental information in `dataclass` format
You can use `FundamentalData` class that will return company fundamental
information in `dataclass` format. Each instance of `CompanyFinancials`
contains an instance of `FundamentalData` in its `data` property.

```python
from ib_fundamental.fundamental import FundamentalData
Expand Down Expand Up @@ -186,8 +195,57 @@ from ib_fundamental.fundamental import FundamentalData
'revenue_q',
'revenue_ttm']

# get quarterly revenue
aapl.data.revenue_q

[Revenue(as_of_date=datetime.datetime(2024, 3, 31, 0, 0), report_type='R', period='3M', revenue=90753000000.0),
Revenue(as_of_date=datetime.datetime(2023, 12, 31, 0, 0), report_type='R', period='3M', revenue=119575000000.0),
Revenue(as_of_date=datetime.datetime(2023, 9, 30, 0, 0), report_type='R', period='3M', revenue=89498000000.0),
Revenue(as_of_date=datetime.datetime(2023, 6, 30, 0, 0), report_type='R', period='3M', revenue=81797000000.0),
Revenue(as_of_date=datetime.datetime(2023, 3, 31, 0, 0), report_type='R', period='3M', revenue=94836000000.0),
Revenue(as_of_date=datetime.datetime(2022, 12, 31, 0, 0), report_type='R', period='3M', revenue=117154000000.0),
Revenue(as_of_date=datetime.datetime(2022, 9, 30, 0, 0), report_type='R', period='3M', revenue=90146000000.0),
Revenue(as_of_date=datetime.datetime(2022, 6, 30, 0, 0), report_type='R', period='3M', revenue=82959000000.0),
Revenue(as_of_date=datetime.datetime(2022, 3, 31, 0, 0), report_type='R', period='3M', revenue=97278000000.0),
Revenue(as_of_date=datetime.datetime(2021, 12, 31, 0, 0), report_type='R', period='3M', revenue=123945000000.0),
Revenue(as_of_date=datetime.datetime(2021, 9, 30, 0, 0), report_type='R', period='3M', revenue=83360000000.0),
Revenue(as_of_date=datetime.datetime(2021, 6, 30, 0, 0), report_type='R', period='3M', revenue=81434000000.0),
Revenue(as_of_date=datetime.datetime(2021, 3, 31, 0, 0), report_type='R', period='3M', revenue=89584000000.0),
Revenue(as_of_date=datetime.datetime(2020, 12, 31, 0, 0), report_type='R', period='3M', revenue=111439000000.0),
Revenue(as_of_date=datetime.datetime(2020, 9, 30, 0, 0), report_type='R', period='3M', revenue=64698000000.0),
Revenue(as_of_date=datetime.datetime(2020, 6, 30, 0, 0), report_type='R', period='3M', revenue=59685000000.0),
Revenue(as_of_date=datetime.datetime(2020, 3, 31, 0, 0), report_type='R', period='3M', revenue=58313000000.0),
Revenue(as_of_date=datetime.datetime(2019, 12, 31, 0, 0), report_type='R', period='3M', revenue=91819000000.0),
Revenue(as_of_date=datetime.datetime(2019, 9, 30, 0, 0), report_type='R', period='3M', revenue=64040000000.0),
Revenue(as_of_date=datetime.datetime(2019, 6, 30, 0, 0), report_type='R', period='3M', revenue=53809000000.0),
Revenue(as_of_date=datetime.datetime(2019, 3, 31, 0, 0), report_type='R', period='3M', revenue=58015000000.0),
Revenue(as_of_date=datetime.datetime(2018, 12, 31, 0, 0), report_type='R', period='3M', revenue=84310000000.0),
Revenue(as_of_date=datetime.datetime(2018, 9, 30, 0, 0), report_type='R', period='3M', revenue=62900000000.0),
Revenue(as_of_date=datetime.datetime(2018, 6, 30, 0, 0), report_type='R', period='3M', revenue=53265000000.0),
Revenue(as_of_date=datetime.datetime(2018, 3, 31, 0, 0), report_type='R', period='3M', revenue=61137000000.0),
Revenue(as_of_date=datetime.datetime(2017, 12, 31, 0, 0), report_type='R', period='3M', revenue=88293000000.0),
Revenue(as_of_date=datetime.datetime(2017, 9, 30, 0, 0), report_type='R', period='3M', revenue=52579000000.0),
Revenue(as_of_date=datetime.datetime(2017, 6, 30, 0, 0), report_type='R', period='3M', revenue=45408000000.0)]

````

## Contributing

If you find a bug please open an issue, pull requests are always welcome.

To develop with `ib_fundamental` please follow these steps

```bash
git clone https://github.com/quantbelt/ib_fundamental.git
cd ib_fundamental
# install development dependencies
pip install .[dev]
# do your things
# run linters and code quality checks
pre-commit
# run tests with tox, requires pypy310,py{310,311,312}
tox
```

[reqFundamental]: https://ib-api-reloaded.github.io/ib_async/api.html#ib_async.ib.IB.reqFundamentalData
[fin_ratios]: http://web.archive.org/web/20200725010343/https://interactivebrokers.github.io/tws-api/fundamental_ratios_tags.html
4 changes: 2 additions & 2 deletions ib_fundamental/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# under the License.

"""IB Fundamental data"""
__all__ = ["CompanyFinancials", "objects", "utils"]
__all__ = ["CompanyFinancials", "FundamentalData", "objects", "utils"]
__author__ = "Gonzalo Sáenz"
__copyright__ = "Copyright 2024 Gonzalo Sáenz"
__credits__ = ["Gonzalo Sáenz"]
Expand All @@ -27,4 +27,4 @@


from . import objects, utils
from .fundamental import CompanyFinancials
from .fundamental import CompanyFinancials, FundamentalData
27 changes: 24 additions & 3 deletions ib_fundamental/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@
# under the License.

"""
Created on Thu May 9 18:21:58 2021
@author: gnzsnz
ib_fundamental utility functions
"""

import dataclasses
import datetime
import json
import re
from typing import Any, Optional

from ib_async import FundamentalRatios
from pandas import DataFrame

re_pattern = re.compile(r"(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])")
Expand All @@ -48,3 +50,22 @@ def camel_to_snake(camel: str) -> str:
"""Convert CamelCase to snake_case"""
# https://stackoverflow.com/questions/1175208/elegant-python-function-to-convert-camelcase-to-snake-case
return re_pattern.sub("_", camel).lower()


def to_json(obj: Any, **kwargs: Any) -> str:
"""Convert FundamentalData attributes to JSON"""

class EnhancedJSONEncoder(json.JSONEncoder):
"""JSON encoder for dataclasses and datetime"""

def default(self, o):
if dataclasses.is_dataclass(o):
return dataclasses.asdict(o)
elif isinstance(o, (datetime.date, datetime.datetime)):
return o.isoformat()
elif isinstance(o, FundamentalRatios):
return vars(o)

return super().default(o)

return json.dumps(obj, cls=EnhancedJSONEncoder, **kwargs)
47 changes: 47 additions & 0 deletions tests/utils_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

"""Tests for ib_fundamental utils"""

import pytest

from ib_fundamental import FundamentalData
from ib_fundamental.utils import to_json

fund_data_methods = (
_m
for _m in dir(FundamentalData)
if (_m[:1] != "_" and _m not in ("client", "parser"))
)


@pytest.fixture(params=fund_data_methods)
def fund_method(fundamental_data, request):
"""JSON fixture, send all FundamentalData methods as json"""
_m = request.param
yield fundamental_data, _m


class TestUtils:
"""Tests for utils module"""

def test_json_inc_annual(self, fund_method):
"""test json"""
_fund_data, _method = fund_method
_json = to_json(getattr(_fund_data, _method))
# assert
assert isinstance(_json, str)

0 comments on commit 5405b68

Please sign in to comment.