Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Json output #4

Merged
merged 7 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Loading