openbb-fd 1.0.0__tar.gz
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.
- openbb_fd-1.0.0/LICENSE +73 -0
- openbb_fd-1.0.0/PKG-INFO +69 -0
- openbb_fd-1.0.0/README.md +53 -0
- openbb_fd-1.0.0/openbb_fd/__init__.py +17 -0
- openbb_fd-1.0.0/openbb_fd/models/__init__.py +1 -0
- openbb_fd-1.0.0/openbb_fd/models/crypto_search.py +92 -0
- openbb_fd-1.0.0/openbb_fd/models/equity_search.py +169 -0
- openbb_fd-1.0.0/openbb_fd/models/etf_search.py +98 -0
- openbb_fd-1.0.0/openbb_fd/utils/__init__.py +1 -0
- openbb_fd-1.0.0/openbb_fd/utils/helpers.py +63 -0
- openbb_fd-1.0.0/pyproject.toml +36 -0
openbb_fd-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Licenses
|
|
2
|
+
|
|
3
|
+
## openbb-fd License
|
|
4
|
+
|
|
5
|
+
MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2023 Theodore Aptekarev.
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
|
|
27
|
+
## OpenBB License
|
|
28
|
+
|
|
29
|
+
MIT License
|
|
30
|
+
|
|
31
|
+
Copyright (c) 2021-2023 OpenBB Inc.
|
|
32
|
+
|
|
33
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
34
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
35
|
+
in the Software without restriction, including without limitation the rights
|
|
36
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
37
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
38
|
+
furnished to do so, subject to the following conditions:
|
|
39
|
+
|
|
40
|
+
The above copyright notice and this permission notice shall be included in all
|
|
41
|
+
copies or substantial portions of the Software.
|
|
42
|
+
|
|
43
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
44
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
45
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
46
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
47
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
48
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
49
|
+
SOFTWARE.
|
|
50
|
+
|
|
51
|
+
## Finance Database License
|
|
52
|
+
|
|
53
|
+
MIT License
|
|
54
|
+
|
|
55
|
+
Copyright (c) 2023 Jeroen Bouma
|
|
56
|
+
|
|
57
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
58
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
59
|
+
in the Software without restriction, including without limitation the rights
|
|
60
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
61
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
62
|
+
furnished to do so, subject to the following conditions:
|
|
63
|
+
|
|
64
|
+
The above copyright notice and this permission notice shall be included in all
|
|
65
|
+
copies or substantial portions of the Software.
|
|
66
|
+
|
|
67
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
68
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
69
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
70
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
71
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
72
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
73
|
+
SOFTWARE.
|
openbb_fd-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: openbb-fd
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Integration of Jeroen Bouma's Finance Database with OpenBB
|
|
5
|
+
Author: piiq
|
|
6
|
+
Author-email: piiq@tinag.ru
|
|
7
|
+
Requires-Python: >=3.8,<3.12
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Requires-Dist: openbb-core (>=1.0.1,<2.0.0)
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# Finance Database provider for OpenBB
|
|
17
|
+
|
|
18
|
+
Finance Database by Jeroen Bouma offers an extensive collection of over 300,000 symbols across various financial instruments, including Equities, ETFs, Funds, Indices, Currencies, Cryptocurrencies, and Money Markets. It's designed to provide a comprehensive overview of sectors, industries, and types of investments.
|
|
19
|
+
|
|
20
|
+
## Introduction
|
|
21
|
+
|
|
22
|
+
This extension integrates the Finance Database datasets into OpenBB. It enhances the capabilities of OpenBB’s equity, crypto, and ETF search functionalities. This integration adds capabilities to search through a broad spectrum of financial instruments.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
To install this extension, you can use pip:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install openbb-fd
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Alternatively, for the latest version directly from the repository:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install git+https://github.com/piiq/openbb-fd.git
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
Here are some command examples to demonstrate the usage of this integration:
|
|
41
|
+
|
|
42
|
+
To search for equities in a defense industry:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
>>> sample = obb.equity.search(query="Defense", provider="fd")
|
|
46
|
+
>>> print(f"Total companies in the sample: {len(sample.to_df()['name'].unique())}")
|
|
47
|
+
Total companies in the sample: 880
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
To find ETFs matching certain criteria:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
>>> sample = obb.etf.search(query="Wheat", provider="fd")
|
|
54
|
+
>>> print(f"Total etfs in the sample: {len(sample.to_df()['name'].unique())}")
|
|
55
|
+
Total etfs in the sample: 10
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For cryptocurrency analysis:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
>>> sample = obb.crypto.search(query="node", provider="fd")
|
|
62
|
+
>>> print(f"Total cryptos in the sample: {len(sample.to_df()['name'].unique())}")
|
|
63
|
+
Total cryptos in the sample: 10
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
This project is licensed under the MIT License.
|
|
69
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Finance Database provider for OpenBB
|
|
2
|
+
|
|
3
|
+
Finance Database by Jeroen Bouma offers an extensive collection of over 300,000 symbols across various financial instruments, including Equities, ETFs, Funds, Indices, Currencies, Cryptocurrencies, and Money Markets. It's designed to provide a comprehensive overview of sectors, industries, and types of investments.
|
|
4
|
+
|
|
5
|
+
## Introduction
|
|
6
|
+
|
|
7
|
+
This extension integrates the Finance Database datasets into OpenBB. It enhances the capabilities of OpenBB’s equity, crypto, and ETF search functionalities. This integration adds capabilities to search through a broad spectrum of financial instruments.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
To install this extension, you can use pip:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install openbb-fd
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Alternatively, for the latest version directly from the repository:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install git+https://github.com/piiq/openbb-fd.git
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
Here are some command examples to demonstrate the usage of this integration:
|
|
26
|
+
|
|
27
|
+
To search for equities in a defense industry:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
>>> sample = obb.equity.search(query="Defense", provider="fd")
|
|
31
|
+
>>> print(f"Total companies in the sample: {len(sample.to_df()['name'].unique())}")
|
|
32
|
+
Total companies in the sample: 880
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
To find ETFs matching certain criteria:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
>>> sample = obb.etf.search(query="Wheat", provider="fd")
|
|
39
|
+
>>> print(f"Total etfs in the sample: {len(sample.to_df()['name'].unique())}")
|
|
40
|
+
Total etfs in the sample: 10
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
For cryptocurrency analysis:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
>>> sample = obb.crypto.search(query="node", provider="fd")
|
|
47
|
+
>>> print(f"Total cryptos in the sample: {len(sample.to_df()['name'].unique())}")
|
|
48
|
+
Total cryptos in the sample: 10
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## License
|
|
52
|
+
|
|
53
|
+
This project is licensed under the MIT License.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Finance Database Provider module."""
|
|
2
|
+
from openbb_core.provider.abstract.provider import Provider
|
|
3
|
+
|
|
4
|
+
from openbb_fd.models.etf_search import FDEtfSearchFetcher
|
|
5
|
+
from openbb_fd.models.equity_search import FDEquitySearchFetcher
|
|
6
|
+
from openbb_fd.models.crypto_search import FDCryptoSearchFetcher
|
|
7
|
+
|
|
8
|
+
fd_provider = Provider(
|
|
9
|
+
name="fd",
|
|
10
|
+
website="https://github.com/JerBouma/FinanceDatabase",
|
|
11
|
+
description="A community-managed database of 300.000+ tickers fully categorized.",
|
|
12
|
+
fetcher_dict={
|
|
13
|
+
"EtfSearch": FDEtfSearchFetcher,
|
|
14
|
+
"EquitySearch": FDEquitySearchFetcher,
|
|
15
|
+
"CryptoSearch": FDCryptoSearchFetcher,
|
|
16
|
+
},
|
|
17
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""FD data models."""
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""FD Crypto Search."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
|
6
|
+
from openbb_core.provider.standard_models.crypto_search import (
|
|
7
|
+
CryptoSearchData,
|
|
8
|
+
CryptoSearchQueryParams,
|
|
9
|
+
)
|
|
10
|
+
from pydantic import Field
|
|
11
|
+
from ..utils.helpers import get_dataset
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class FDCryptoSearchQueryParams(CryptoSearchQueryParams):
|
|
15
|
+
"""FD Crypto Search Query Params."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FDCryptoSearchData(CryptoSearchData):
|
|
19
|
+
"""FD Crypto Search Data."""
|
|
20
|
+
|
|
21
|
+
__alias_dict__ = {"symbol": "cryptocurrency"}
|
|
22
|
+
|
|
23
|
+
exchange_symbol: Optional[str] = Field(
|
|
24
|
+
description="The exchange symbol of the cryptocurrency.",
|
|
25
|
+
alias="symbol",
|
|
26
|
+
default=None,
|
|
27
|
+
)
|
|
28
|
+
currency: Optional[str] = Field(
|
|
29
|
+
description="The currency the crypto is traded in.",
|
|
30
|
+
)
|
|
31
|
+
description: Optional[str] = Field(
|
|
32
|
+
description="A description of the crypto.",
|
|
33
|
+
alias="summary",
|
|
34
|
+
default=None,
|
|
35
|
+
)
|
|
36
|
+
exchange: Optional[str] = Field(
|
|
37
|
+
description="The exchange code the crypto trades on.",
|
|
38
|
+
default=None,
|
|
39
|
+
)
|
|
40
|
+
market: Optional[str] = Field(
|
|
41
|
+
description="The market the crypto trades on.",
|
|
42
|
+
default=None,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class FDCryptoSearchFetcher(
|
|
47
|
+
Fetcher[
|
|
48
|
+
FDCryptoSearchQueryParams,
|
|
49
|
+
List[FDCryptoSearchData],
|
|
50
|
+
]
|
|
51
|
+
):
|
|
52
|
+
"""Transform the query, extract and transform the data from the FD endpoints."""
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def transform_query(params: Dict[str, Any]) -> FDCryptoSearchQueryParams:
|
|
56
|
+
"""Transform the query."""
|
|
57
|
+
return FDCryptoSearchQueryParams(**params)
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def extract_data( # pylint: disable=unused-argument
|
|
61
|
+
query: FDCryptoSearchQueryParams,
|
|
62
|
+
credentials: Optional[Dict[str, str]],
|
|
63
|
+
**kwargs: Any,
|
|
64
|
+
) -> List[Dict]:
|
|
65
|
+
"""Return the raw data from the FD endpoint."""
|
|
66
|
+
cryptos = get_dataset("cryptos")
|
|
67
|
+
|
|
68
|
+
if query.query:
|
|
69
|
+
cryptos = cryptos[
|
|
70
|
+
cryptos["symbol"].str.contains(query.query, case=False)
|
|
71
|
+
| cryptos["cryptocurrency"].str.contains(query.query, case=False)
|
|
72
|
+
| cryptos["name"].str.contains(query.query, case=False)
|
|
73
|
+
| cryptos["currency"].str.contains(query.query, case=False)
|
|
74
|
+
| cryptos["summary"].str.contains(query.query, case=False)
|
|
75
|
+
| cryptos["exchange"].str.contains(query.query, case=False)
|
|
76
|
+
| cryptos["market"].str.contains(query.query, case=False)
|
|
77
|
+
]
|
|
78
|
+
for col in cryptos:
|
|
79
|
+
if cryptos[col].dtype in ("int", "float"):
|
|
80
|
+
cryptos[col] = cryptos[col].fillna(0)
|
|
81
|
+
elif cryptos[col].dtype == "string":
|
|
82
|
+
cryptos[col] = cryptos[col].fillna("")
|
|
83
|
+
return cryptos.to_dict("records")
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def transform_data( # pylint: disable=unused-argument
|
|
87
|
+
query: FDCryptoSearchQueryParams,
|
|
88
|
+
data: List[Dict],
|
|
89
|
+
**kwargs: Any,
|
|
90
|
+
) -> List[FDCryptoSearchData]:
|
|
91
|
+
"""Return the transformed data."""
|
|
92
|
+
return [FDCryptoSearchData.model_validate(d) for d in data]
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""FD Equity Search."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
|
6
|
+
from openbb_core.provider.standard_models.equity_search import (
|
|
7
|
+
EquitySearchData,
|
|
8
|
+
EquitySearchQueryParams,
|
|
9
|
+
)
|
|
10
|
+
from pydantic import Field
|
|
11
|
+
from ..utils.helpers import get_dataset
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class FDEquitySearchQueryParams(EquitySearchQueryParams):
|
|
15
|
+
"""FD Equity Search Query Params."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FDEquitySearchData(EquitySearchData):
|
|
19
|
+
"""FD Equity Search Data."""
|
|
20
|
+
|
|
21
|
+
description: Optional[str] = Field(
|
|
22
|
+
description="A description of the company.",
|
|
23
|
+
alias="summary",
|
|
24
|
+
default=None,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
currency: Optional[str] = Field(
|
|
28
|
+
description="The currency the equity is traded in.",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
sector: Optional[str] = Field(
|
|
32
|
+
description="The sector the company belongs to.",
|
|
33
|
+
default=None,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
industry_group: Optional[str] = Field(
|
|
37
|
+
description="The industry group the company belongs to.",
|
|
38
|
+
default=None,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
industry: Optional[str] = Field(
|
|
42
|
+
description="The industry the company belongs to.",
|
|
43
|
+
default=None,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
exchange: Optional[str] = Field(
|
|
47
|
+
description="The exchange code the equity trades on.",
|
|
48
|
+
default=None,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
market: Optional[str] = Field(
|
|
52
|
+
description="The market the equity trades on.",
|
|
53
|
+
default=None,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
country: Optional[str] = Field(
|
|
57
|
+
description="The country of the company's address.",
|
|
58
|
+
default=None,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
state: Optional[str] = Field(
|
|
62
|
+
description="The state of the company's address.",
|
|
63
|
+
default=None,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
city: Optional[str] = Field(
|
|
67
|
+
description="The city of the company's address.",
|
|
68
|
+
default=None,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
zipcode: Optional[str] = Field(
|
|
72
|
+
description="The zipcode of the company's address.",
|
|
73
|
+
default=None,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
website: Optional[str] = Field(
|
|
77
|
+
description="The website of the company.",
|
|
78
|
+
default=None,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
market_cap: Optional[str] = Field(
|
|
82
|
+
description="The market cap of the company.",
|
|
83
|
+
default=None,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
isin: Optional[str] = Field(
|
|
87
|
+
description="The ISIN of the equity.",
|
|
88
|
+
default=None,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
cusip: Optional[str] = Field(
|
|
92
|
+
description="The CUSIP of the equity.",
|
|
93
|
+
default=None,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
figi: Optional[str] = Field(
|
|
97
|
+
description="The FIGI of the equity.",
|
|
98
|
+
default=None,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
composite_figi: Optional[str] = Field(
|
|
102
|
+
description="The composite FIGI of the equity.",
|
|
103
|
+
default=None,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
shareclass_figi: Optional[str] = Field(
|
|
107
|
+
description="The shareclass FIGI of the equity.",
|
|
108
|
+
default=None,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class FDEquitySearchFetcher(
|
|
113
|
+
Fetcher[
|
|
114
|
+
FDEquitySearchQueryParams,
|
|
115
|
+
List[FDEquitySearchData],
|
|
116
|
+
]
|
|
117
|
+
):
|
|
118
|
+
"""Transform the query, extract and transform the data from the FD endpoints."""
|
|
119
|
+
|
|
120
|
+
@staticmethod
|
|
121
|
+
def transform_query(params: Dict[str, Any]) -> FDEquitySearchQueryParams:
|
|
122
|
+
"""Transform the query."""
|
|
123
|
+
return FDEquitySearchQueryParams(**params)
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def extract_data( # pylint: disable=unused-argument
|
|
127
|
+
query: FDEquitySearchQueryParams,
|
|
128
|
+
credentials: Optional[Dict[str, str]],
|
|
129
|
+
**kwargs: Any,
|
|
130
|
+
) -> List[Dict]:
|
|
131
|
+
"""Return the raw data from the FD endpoint."""
|
|
132
|
+
equities = get_dataset("equities")
|
|
133
|
+
|
|
134
|
+
if query.query:
|
|
135
|
+
equities = equities[
|
|
136
|
+
equities["symbol"].str.contains(query.query, case=False)
|
|
137
|
+
| equities["name"].str.contains(query.query, case=False)
|
|
138
|
+
| equities["summary"].str.contains(query.query, case=False)
|
|
139
|
+
| equities["currency"].str.contains(query.query, case=False)
|
|
140
|
+
| equities["sector"].str.contains(query.query, case=False)
|
|
141
|
+
| equities["industry_group"].str.contains(query.query, case=False)
|
|
142
|
+
| equities["industry"].str.contains(query.query, case=False)
|
|
143
|
+
| equities["exchange"].str.contains(query.query, case=False)
|
|
144
|
+
| equities["market"].str.contains(query.query, case=False)
|
|
145
|
+
| equities["country"].str.contains(query.query, case=False)
|
|
146
|
+
| equities["state"].str.contains(query.query, case=False)
|
|
147
|
+
| equities["city"].str.contains(query.query, case=False)
|
|
148
|
+
| equities["zipcode"].str.contains(query.query, case=False)
|
|
149
|
+
| equities["website"].str.contains(query.query, case=False)
|
|
150
|
+
| equities["market_cap"].str.contains(query.query, case=False)
|
|
151
|
+
| equities["isin"].str.contains(query.query, case=False)
|
|
152
|
+
| equities["cusip"].str.contains(query.query, case=False)
|
|
153
|
+
| equities["figi"].str.contains(query.query, case=False)
|
|
154
|
+
| equities["composite_figi"].str.contains(query.query, case=False)
|
|
155
|
+
| equities["shareclass_figi"].str.contains(query.query, case=False)
|
|
156
|
+
]
|
|
157
|
+
for col in equities:
|
|
158
|
+
if equities[col].dtype in ("int", "float"):
|
|
159
|
+
equities[col] = equities[col].fillna(0)
|
|
160
|
+
elif equities[col].dtype == "string":
|
|
161
|
+
equities[col] = equities[col].fillna("")
|
|
162
|
+
return equities.to_dict("records")
|
|
163
|
+
|
|
164
|
+
@staticmethod
|
|
165
|
+
def transform_data( # pylint: disable=unused-argument
|
|
166
|
+
query: FDEquitySearchQueryParams, data: List[Dict], **kwargs: Any
|
|
167
|
+
) -> List[FDEquitySearchData]:
|
|
168
|
+
"""Return the transformed data."""
|
|
169
|
+
return [FDEquitySearchData.model_validate(d) for d in data]
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""FD ETF Search."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
# import pandas as pd
|
|
6
|
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
|
7
|
+
from openbb_core.provider.standard_models.etf_search import (
|
|
8
|
+
EtfSearchData,
|
|
9
|
+
EtfSearchQueryParams,
|
|
10
|
+
)
|
|
11
|
+
from pydantic import Field
|
|
12
|
+
from ..utils.helpers import get_dataset
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FDEtfSearchQueryParams(EtfSearchQueryParams):
|
|
16
|
+
"""FD ETF Search Query Params."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class FDEtfSearchData(EtfSearchData):
|
|
20
|
+
"""FD ETF Search Data."""
|
|
21
|
+
|
|
22
|
+
currency: Optional[str] = Field(
|
|
23
|
+
description="The currency the ETF is traded in.",
|
|
24
|
+
)
|
|
25
|
+
description: Optional[str] = Field(
|
|
26
|
+
description="A description of the ETF.",
|
|
27
|
+
alias="summary",
|
|
28
|
+
default=None,
|
|
29
|
+
)
|
|
30
|
+
category_group: Optional[str] = Field(
|
|
31
|
+
description="The category group the ETF belongs to.",
|
|
32
|
+
default=None,
|
|
33
|
+
)
|
|
34
|
+
category: Optional[str] = Field(
|
|
35
|
+
description="The category the ETF belongs to.",
|
|
36
|
+
default=None,
|
|
37
|
+
)
|
|
38
|
+
family: Optional[str] = Field(
|
|
39
|
+
description="The family the ETF belongs to.",
|
|
40
|
+
default=None,
|
|
41
|
+
)
|
|
42
|
+
exchange: Optional[str] = Field(
|
|
43
|
+
description="The exchange code the ETF trades on.",
|
|
44
|
+
default=None,
|
|
45
|
+
)
|
|
46
|
+
market: Optional[str] = Field(
|
|
47
|
+
description="The market the ETF trades on.",
|
|
48
|
+
default=None,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class FDEtfSearchFetcher(
|
|
53
|
+
Fetcher[
|
|
54
|
+
FDEtfSearchQueryParams,
|
|
55
|
+
List[FDEtfSearchData],
|
|
56
|
+
]
|
|
57
|
+
):
|
|
58
|
+
"""Transform the query, extract and transform the data from the FD endpoints."""
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def transform_query(params: Dict[str, Any]) -> FDEtfSearchQueryParams:
|
|
62
|
+
"""Transform the query."""
|
|
63
|
+
return FDEtfSearchQueryParams(**params)
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def extract_data( # pylint: disable=unused-argument
|
|
67
|
+
query: FDEtfSearchQueryParams,
|
|
68
|
+
credentials: Optional[Dict[str, str]],
|
|
69
|
+
**kwargs: Any,
|
|
70
|
+
) -> List[Dict]:
|
|
71
|
+
"""Return the raw data from the FD endpoint."""
|
|
72
|
+
etfs = get_dataset("etfs")
|
|
73
|
+
|
|
74
|
+
if query.query:
|
|
75
|
+
etfs = etfs[
|
|
76
|
+
etfs["symbol"].str.contains(query.query, case=False)
|
|
77
|
+
| etfs["name"].str.contains(query.query, case=False)
|
|
78
|
+
| etfs["currency"].str.contains(query.query, case=False)
|
|
79
|
+
| etfs["summary"].str.contains(query.query, case=False)
|
|
80
|
+
| etfs["category_group"].str.contains(query.query, case=False)
|
|
81
|
+
| etfs["category"].str.contains(query.query, case=False)
|
|
82
|
+
| etfs["family"].str.contains(query.query, case=False)
|
|
83
|
+
| etfs["exchange"].str.contains(query.query, case=False)
|
|
84
|
+
| etfs["market"].str.contains(query.query, case=False)
|
|
85
|
+
]
|
|
86
|
+
for col in etfs:
|
|
87
|
+
if etfs[col].dtype in ("int", "float"):
|
|
88
|
+
etfs[col] = etfs[col].fillna(0)
|
|
89
|
+
elif etfs[col].dtype == "string":
|
|
90
|
+
etfs[col] = etfs[col].fillna("")
|
|
91
|
+
return etfs.to_dict("records")
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def transform_data( # pylint: disable=unused-argument
|
|
95
|
+
query: FDEtfSearchQueryParams, data: List[Dict], **kwargs: Any
|
|
96
|
+
) -> List[FDEtfSearchData]:
|
|
97
|
+
"""Return the transformed data."""
|
|
98
|
+
return [FDEtfSearchData.model_validate(d) for d in data]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""FD package utils."""
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Helper for downloading data to a local location."""
|
|
2
|
+
import os
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
GITHUB_ROOT_URL = "https://raw.githubusercontent.com/"
|
|
9
|
+
GITHUB_DATA_FOLDER = "JerBouma/FinanceDatabase/main/compression/"
|
|
10
|
+
LOCAL_FOLDER = os.path.join(os.path.dirname(__file__), "..", "data")
|
|
11
|
+
|
|
12
|
+
_dataset = Literal[
|
|
13
|
+
"cryptos", "currencies", "equities", "etfs", "funds", "indices", "moneymarkets"
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _get_github_etag(dataset: _dataset) -> str:
|
|
18
|
+
"""Get ETag for a dataset on GitHub."""
|
|
19
|
+
url = GITHUB_ROOT_URL + GITHUB_DATA_FOLDER + f"{dataset}.bz2"
|
|
20
|
+
response = requests.head(url, timeout=1)
|
|
21
|
+
return response.headers.get("ETag", "")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _get_saved_etag(dataset: _dataset) -> str:
|
|
25
|
+
"""Get saved ETag for a dataset."""
|
|
26
|
+
etag_file = os.path.join(LOCAL_FOLDER, f"{dataset}.etag")
|
|
27
|
+
if os.path.exists(etag_file):
|
|
28
|
+
with open(etag_file, "r", encoding="utf-8") as f:
|
|
29
|
+
return f.read().strip()
|
|
30
|
+
return ""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _save_etag(dataset: _dataset, etag: str) -> None:
|
|
34
|
+
"""Save ETag for a dataset."""
|
|
35
|
+
etag_file = os.path.join(LOCAL_FOLDER, f"{dataset}.etag")
|
|
36
|
+
with open(etag_file, "w", encoding="utf-8") as f:
|
|
37
|
+
f.write(etag)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _download_and_save(dataset: _dataset) -> None:
|
|
41
|
+
"""Download and save dataset from GitHub."""
|
|
42
|
+
if not os.path.exists(LOCAL_FOLDER):
|
|
43
|
+
os.makedirs(LOCAL_FOLDER)
|
|
44
|
+
print(f"Downloading {dataset}...")
|
|
45
|
+
url = GITHUB_ROOT_URL + GITHUB_DATA_FOLDER + f"{dataset}.bz2"
|
|
46
|
+
data = pd.read_csv(url, compression="bz2", header=0)
|
|
47
|
+
data.to_csv(os.path.join(LOCAL_FOLDER, f"{dataset}.csv"), index=False)
|
|
48
|
+
etag = _get_github_etag(dataset)
|
|
49
|
+
if etag:
|
|
50
|
+
_save_etag(dataset, etag)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_dataset(dataset: _dataset) -> pd.DataFrame:
|
|
54
|
+
"""Get dataset from the data directory."""
|
|
55
|
+
local_path = os.path.join(LOCAL_FOLDER, f"{dataset}.csv")
|
|
56
|
+
if not os.path.exists(local_path):
|
|
57
|
+
_download_and_save(dataset)
|
|
58
|
+
else:
|
|
59
|
+
saved_etag = _get_saved_etag(dataset)
|
|
60
|
+
current_etag = _get_github_etag(dataset)
|
|
61
|
+
if saved_etag != current_etag:
|
|
62
|
+
_download_and_save(dataset)
|
|
63
|
+
return pd.read_csv(local_path, header=0).convert_dtypes()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "openbb-fd"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Integration of Jeroen Bouma's Finance Database with OpenBB"
|
|
5
|
+
authors = ["piiq <piiq@tinag.ru>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
packages = [{ include = "openbb_fd" }]
|
|
8
|
+
|
|
9
|
+
[tool.poetry.dependencies]
|
|
10
|
+
python = "^3.8,<3.12"
|
|
11
|
+
openbb-core = "^1.0.1"
|
|
12
|
+
|
|
13
|
+
[tool.poetry.group.dev.dependencies]
|
|
14
|
+
openbb-devtools = "^1.0.0"
|
|
15
|
+
openbb = "^4.0.1"
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["poetry-core"]
|
|
19
|
+
build-backend = "poetry.core.masonry.api"
|
|
20
|
+
|
|
21
|
+
[tool.poetry.plugins."openbb_provider_extension"]
|
|
22
|
+
fd = "openbb_fd:fd_provider"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
[[tool.poetry.source]]
|
|
26
|
+
name = "PyPI"
|
|
27
|
+
priority = "primary"
|
|
28
|
+
|
|
29
|
+
[[tool.poetry.source]]
|
|
30
|
+
name = "testpypi"
|
|
31
|
+
url = "https://test.pypi.org/simple/"
|
|
32
|
+
priority = "supplemental"
|
|
33
|
+
|
|
34
|
+
[tool.ruff]
|
|
35
|
+
line-length = 122
|
|
36
|
+
target-version = "py38"
|