diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 1d002032d50..0a776008049 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -3,21 +3,16 @@ tag-template: 'v$NEXT_MINOR_VERSION' exclude-contributors: - 'jmaslek' - 'DidierRLopes' - - 'JerBouma' - 'deeleeramone' - 'hjoaquim' - 'jose-donato' - 'luqmanbello' - - 'martinb-bb' - 'montezdesousa' - 'tehcoderer' - - 'Chavithra' - 'colin99d' - - 'northern-64bit' - 'piiq' - 'andrewkenreich' - 'IgorWounds' - - 'jerryduluk' - 'minhhoang1023' template: | diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 419ad3ca212..217cadebb82 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -63,3 +63,15 @@ jobs: uses: docker://avtodev/markdown-lint:v1 with: args: "./*.md ./changelogs/*.md ./openbb_terminal/**/*.md ./discordbot/**/*.md" + + json-yaml-validate: + name: JSON Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: json-yaml-validate + id: json-yaml-validate + uses: GrantBirki/json-yaml-validate@v2.0.0 + with: + exclude_file: json_validate_exclude.txt \ No newline at end of file diff --git a/.github/workflows/sdkv4-core.yml b/.github/workflows/sdkv4-core.yml new file mode 100644 index 00000000000..31cfb32bde2 --- /dev/null +++ b/.github/workflows/sdkv4-core.yml @@ -0,0 +1,35 @@ +name: Test SDKV4 + +on: + pull_request: + branches: + - feature/openbb-sdk-v4 + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + +jobs: + unit_tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python_version: + ["3.8", "3.9", "3.10", "3.11"] + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.ref }} + - name: Install Python ${{ matrix.python_version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python_version }} + allow-prereleases: true + cache: "pip" + + - name: Run tests + run: | + pip install nox + nox -s tests --python ${{ matrix.python_version }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6a09d373655..d2b90393c38 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1212,6 +1212,9 @@ In order to do that, you'll simply need to choose from one the following files: 1. [local_credentials.json](openbb_terminal/miscellaneous/models/local_credentials.json) --> credentials that should only be stored locally and not pushed to the [OpenBB Hub](https://my.openbb.co/) like brokerage keys or other very sensitive or personal to the user. 2. [hub_credentials.json](openbb_terminal/miscellaneous/models/hub_credentials.json) --> credentials that should be stored in the [OpenBB Hub](https://my.openbb.co/) like API keys to access your favorite providers. +Then just update [all_api_keys.json](openbb_terminal/miscellaneous/models/all_api_keys.json) with the instructions to get +the api key from the data source website. Make sure that this file has the correct `.json` format, otherwise the API keys page in the Hub will break (e.g. in json the last element key-value pair shouldn't be followed by a comma, and the last object in a list of dictionaries should also not be followed by a comma). + > Note: By differentiating between local and hub credentials, we can ensure that the user's credentials are not pushed to the [OpenBB Hub](https://my.openbb.co/) and are only stored locally. This does not mean that the credentials are not secure in the OpenBB Hub, but rather that the user can choose to store them locally if they wish. ### Setting and checking API key diff --git a/frontend-components/tables/src/components/Table/index.tsx b/frontend-components/tables/src/components/Table/index.tsx index e948185ece6..8446893f1c8 100644 --- a/frontend-components/tables/src/components/Table/index.tsx +++ b/frontend-components/tables/src/components/Table/index.tsx @@ -181,7 +181,8 @@ export default function Table({ : columns[0]; const indexValue = indexLabel ? row[indexLabel] : null; const value = row[column]; - const only_numbers = value?.toString().replace(/[^0-9]/g, "") ?? ""; + const only_numbers = + value?.toString()?.split(".")?.[0]?.replace(/[^0-9]/g, "") ?? ""; const probablyDate = only_numbers?.length >= 4 && (includesDateNames(column) || @@ -197,7 +198,11 @@ export default function Table({ indexValue.toLowerCase().includes("hour") || indexValue.toLowerCase().includes("minute")))); - if (probablyDate && isoYearRegex.test(value?.toString())) + if ( + probablyDate && + value?.length === 4 && + isoYearRegex.test(value?.toString()) + ) return value; if (probablyDate) { @@ -219,7 +224,8 @@ export default function Table({ const indexValue = indexLabel ? row.original[indexLabel] : null; const value = row.original[column]; const valueType = typeof value; - const only_numbers = value?.toString().replace(/[^0-9]/g, "") ?? ""; + const only_numbers = + value?.toString()?.split(".")?.[0]?.replace(/[^0-9]/g, "") ?? ""; const probablyDate = only_numbers?.length >= 4 && (includesDateNames(column) || @@ -246,14 +252,15 @@ export default function Table({ ); } - if (probablyDate && isoYearRegex.test(value?.toString())) { - return
{value}
; - } + if ( probablyDate && - !isNaN(new Date(value).getTime()) && - !isoYearRegex.test(value?.toString()) + value?.length === 4 && + isoYearRegex.test(value?.toString()) ) { + return{value}
; + } + if (probablyDate && !isNaN(new Date(value).getTime())) { if (typeof value === "string") { const date = value.split("T")[0]; const time = value.split("T")[1]?.split(".")[0]; diff --git a/json_validate_exclude.txt b/json_validate_exclude.txt new file mode 100644 index 00000000000..da93f0e08ad --- /dev/null +++ b/json_validate_exclude.txt @@ -0,0 +1,3 @@ +tests/ +openbb_terminal/miscellaneous/gpt_index/index_0.4.json +.devcontainer/devcontainer.json \ No newline at end of file diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 00000000000..9f92d0e0f6e --- /dev/null +++ b/noxfile.py @@ -0,0 +1,8 @@ +import nox + + +@nox.session(python=["3.8", "3.9", "3.10", "3.11"]) +def tests(session): + session.install("./openbb_sdk") + session.install("pytest") + session.run("pytest", "openbb_sdk/sdk/core/tests") diff --git a/openbb_sdk/extensions/charting/openbb_charting/__init__.py b/openbb_sdk/extensions/charting/openbb_charting/__init__.py index 9d60f0e2e6f..494d7d40b75 100644 --- a/openbb_sdk/extensions/charting/openbb_charting/__init__.py +++ b/openbb_sdk/extensions/charting/openbb_charting/__init__.py @@ -3,4 +3,4 @@ from openbb_charting.core.backend import ( create_backend, get_backend, ) -from openbb_charting.core.to_plotly_json import to_plotly_json +from openbb_charting.core.to_chart import to_chart diff --git a/openbb_sdk/extensions/charting/openbb_charting/builder.py b/openbb_sdk/extensions/charting/openbb_charting/builder.py index 75b5710f8bc..90351e6bd5d 100644 --- a/openbb_sdk/extensions/charting/openbb_charting/builder.py +++ b/openbb_sdk/extensions/charting/openbb_charting/builder.py @@ -1,9 +1,12 @@ from inspect import getmembers, isfunction from typing import List +from openbb_charting import charting_router from openbb_core.app.router import RouterLoader -CHARTING_ROUTER = "./charting_router.py" +# refers to the root of the repo, not the root of the extension +# this script should be run from the root of the repo +CHARTING_ROUTER = "openbb_sdk/extensions/charting/openbb_charting/charting_router.py" def get_routes() -> List[str]: @@ -18,15 +21,13 @@ def create_charting_functions_w_routes(routes_to_add: List[str]): f.write( f""" -def {route}(**kwargs): +def {route}(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `{original_route}` does not have a charting function") """ ) def get_charting_functions() -> List[str]: - from . import charting_router # pylint: disable=import-outside-toplevel - functions = getmembers(charting_router, isfunction) if functions: return [function[0] for function in functions] diff --git a/openbb_sdk/extensions/charting/openbb_charting/charting_router.py b/openbb_sdk/extensions/charting/openbb_charting/charting_router.py index 59f640d8f32..10d1b9fcf1a 100644 --- a/openbb_sdk/extensions/charting/openbb_charting/charting_router.py +++ b/openbb_sdk/extensions/charting/openbb_charting/charting_router.py @@ -1,3 +1,7 @@ +from typing import Any, Dict, Tuple + +from openbb_charting.core.openbb_figure import OpenBBFigure +from openbb_charting.core.openbb_figure_table import OpenBBFigureTable from openbb_charting.core.plotly_ta.ta_class import PlotlyTA from openbb_core.app.model.charts.chart import ChartFormat from openbb_core.app.router import Router @@ -14,1645 +18,1801 @@ CHART_FORMAT = ChartFormat.plotly # pylint: disable=too-many-lines -def crypto_load(**kwargs): +def crypto_load(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `crypto/load` does not have a charting function") -def economy_corecpi(**kwargs): +def economy_corecpi(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/corecpi` does not have a charting function" ) -def economy_cpi(**kwargs): +def economy_cpi(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `economy/cpi` does not have a charting function") -def economy_cpi_options(**kwargs): +def economy_cpi_options(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/cpi/options` does not have a charting function" ) -def economy_index(**kwargs): +def economy_index(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/index` does not have a charting function" ) -def economy_available_indices(**kwargs): +def economy_available_indices(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/available/indices` does not have a charting function" ) -def economy_macro(**kwargs): +def economy_macro(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/macro` does not have a charting function" ) -def economy_macro_countries(**kwargs): +def economy_macro_countries(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/macro/countries` does not have a charting function" ) -def economy_macro_parameters(**kwargs): +def economy_macro_parameters(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/macro/parameters` does not have a charting function" ) -def economy_balance(**kwargs): +def economy_balance(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/balance` does not have a charting function" ) -def economy_bigmac(**kwargs): +def economy_bigmac(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/bigmac` does not have a charting function" ) -def economy_country_codes(**kwargs): +def economy_country_codes(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/country/codes` does not have a charting function" ) -def economy_currencies(**kwargs): +def economy_currencies(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/currencies` does not have a charting function" ) -def economy_debt(**kwargs): +def economy_debt(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/debt` does not have a charting function" ) -def economy_events(**kwargs): +def economy_events(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/events` does not have a charting function" ) -def economy_fgdp(**kwargs): +def economy_fgdp(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/fgdp` does not have a charting function" ) -def economy_fred(**kwargs): +def economy_fred(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/fred` does not have a charting function" ) -def economy_fred_search(**kwargs): +def economy_fred_search(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/fred/search` does not have a charting function" ) -def economy_futures(**kwargs): +def economy_futures(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/futures` does not have a charting function" ) -def economy_gdp(**kwargs): +def economy_gdp(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `economy/gdp` does not have a charting function") -def economy_glbonds(**kwargs): +def economy_glbonds(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/glbonds` does not have a charting function" ) -def economy_indices(**kwargs): +def economy_indices(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/indices` does not have a charting function" ) -def economy_overview(**kwargs): +def economy_overview(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/overview` does not have a charting function" ) -def economy_perfmap(**kwargs): +def economy_perfmap(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/perfmap` does not have a charting function" ) -def economy_performance(**kwargs): +def economy_performance(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/performance` does not have a charting function" ) -def economy_revenue(**kwargs): +def economy_revenue(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/revenue` does not have a charting function" ) -def economy_rgdp(**kwargs): +def economy_rgdp(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/rgdp` does not have a charting function" ) -def economy_rtps(**kwargs): +def economy_rtps(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/rtps` does not have a charting function" ) -def economy_search_index(**kwargs): +def economy_search_index(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/search/index` does not have a charting function" ) -def economy_spending(**kwargs): +def economy_spending(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/spending` does not have a charting function" ) -def economy_trust(**kwargs): +def economy_trust(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/trust` does not have a charting function" ) -def economy_usbonds(**kwargs): +def economy_usbonds(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/usbonds` does not have a charting function" ) -def economy_valuation(**kwargs): +def economy_valuation(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `economy/valuation` does not have a charting function" ) -def fixedincome_treasury(**kwargs): +def fixedincome_treasury(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `fixedincome/treasury` does not have a charting function" ) -def fixedincome_ycrv(**kwargs): +def fixedincome_ycrv(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `fixedincome/ycrv` does not have a charting function" ) -def forex_load(**kwargs): +def forex_load(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `forex/load` does not have a charting function") -def news_globalnews(**kwargs): +def news_globalnews(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `news/globalnews` does not have a charting function" ) -def news_sectornews(**kwargs): +def news_sectornews(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `news/sectornews` does not have a charting function" ) -def providers_benzinga_global_news_standardized(**kwargs): +def providers_benzinga_global_news_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/benzinga/global/news/standardized` does not have a charting function" ) -def providers_benzinga_stock_news_standardized(**kwargs): +def providers_benzinga_stock_news_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/benzinga/stock/news/standardized` does not have a charting function" ) -def providers_fmp_key_executives_standardized(**kwargs): +def providers_fmp_key_executives_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/key/executives/standardized` does not have a charting function" ) -def providers_fmp_stock_eod_standardized(**kwargs): +def providers_fmp_stock_eod_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/stock/eod/standardized` does not have a charting function" ) -def providers_fmp_global_news_standardized(**kwargs): +def providers_fmp_global_news_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/global/news/standardized` does not have a charting function" ) -def providers_fmp_stock_news_standardized(**kwargs): +def providers_fmp_stock_news_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/stock/news/standardized` does not have a charting function" ) -def providers_fmp_income_statement_standardized(**kwargs): +def providers_fmp_income_statement_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/income/statement/standardized` does not have a charting function" ) -def providers_fmp_balance_sheet_standardized(**kwargs): +def providers_fmp_balance_sheet_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/balance/sheet/standardized` does not have a charting function" ) -def providers_fmp_cash_flow_statement_standardized(**kwargs): +def providers_fmp_cash_flow_statement_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/cash/flow/statement/standardized` does not have a charting function" ) -def providers_fmp_share_statistics_standardized(**kwargs): +def providers_fmp_share_statistics_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/share/statistics/standardized` does not have a charting function" ) -def providers_fmp_major_indices_eod_standardized(**kwargs): +def providers_fmp_major_indices_eod_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/major/indices/eod/standardized` does not have a charting function" ) -def providers_fmp_revenue_geographic_standardized(**kwargs): +def providers_fmp_revenue_geographic_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/revenue/geographic/standardized` does not have a charting function" ) -def providers_fmp_revenue_business_line_standardized(**kwargs): +def providers_fmp_revenue_business_line_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/revenue/business/line/standardized` does not have a charting function" ) -def providers_fmp_institutional_ownership_standardized(**kwargs): +def providers_fmp_institutional_ownership_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/institutional/ownership/standardized` does not have a charting function" ) -def providers_fmp_company_overview_standardized(**kwargs): +def providers_fmp_company_overview_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/company/overview/standardized` does not have a charting function" ) -def providers_fmp_stock_insider_trading_standardized(**kwargs): +def providers_fmp_stock_insider_trading_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/stock/insider/trading/standardized` does not have a charting function" ) -def providers_fmp_stock_ownership_standardized(**kwargs): +def providers_fmp_stock_ownership_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/stock/ownership/standardized` does not have a charting function" ) -def providers_fmp_e_s_g_score_standardized(**kwargs): +def providers_fmp_e_s_g_score_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/e/s/g/score/standardized` does not have a charting function" ) -def providers_fmp_e_s_g_sector_standardized(**kwargs): +def providers_fmp_e_s_g_sector_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/e/s/g/sector/standardized` does not have a charting function" ) -def providers_fmp_e_s_g_risk_rating_standardized(**kwargs): +def providers_fmp_e_s_g_risk_rating_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/e/s/g/risk/rating/standardized` does not have a charting function" ) -def providers_fmp_stock_price_standardized(**kwargs): +def providers_fmp_stock_price_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/stock/price/standardized` does not have a charting function" ) -def providers_fmp_price_target_consensus_standardized(**kwargs): +def providers_fmp_price_target_consensus_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/price/target/consensus/standardized` does not have a charting function" ) -def providers_fmp_price_target_standardized(**kwargs): +def providers_fmp_price_target_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/price/target/standardized` does not have a charting function" ) -def providers_fmp_analyst_estimates_standardized(**kwargs): +def providers_fmp_analyst_estimates_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/analyst/estimates/standardized` does not have a charting function" ) -def providers_fmp_earnings_calendar_standardized(**kwargs): +def providers_fmp_earnings_calendar_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/earnings/calendar/standardized` does not have a charting function" ) -def providers_fmp_earnings_call_transcript_standardized(**kwargs): +def providers_fmp_earnings_call_transcript_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/earnings/call/transcript/standardized` does not have a charting function" ) -def providers_fmp_historical_stock_splits_standardized(**kwargs): +def providers_fmp_historical_stock_splits_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/historical/stock/splits/standardized` does not have a charting function" ) -def providers_fmp_historical_dividends_standardized(**kwargs): +def providers_fmp_historical_dividends_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/historical/dividends/standardized` does not have a charting function" ) -def providers_fmp_key_metrics_standardized(**kwargs): +def providers_fmp_key_metrics_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/key/metrics/standardized` does not have a charting function" ) -def providers_fmp_s_e_c_filings_standardized(**kwargs): +def providers_fmp_s_e_c_filings_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/s/e/c/filings/standardized` does not have a charting function" ) -def providers_fmp_treasury_rates_standardized(**kwargs): +def providers_fmp_treasury_rates_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/treasury/rates/standardized` does not have a charting function" ) -def providers_fmp_executive_compensation_standardized(**kwargs): +def providers_fmp_executive_compensation_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/executive/compensation/standardized` does not have a charting function" ) -def providers_fmp_crypto_price_standardized(**kwargs): +def providers_fmp_crypto_price_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/crypto/price/standardized` does not have a charting function" ) -def providers_fmp_crypto_eod_standardized(**kwargs): +def providers_fmp_crypto_eod_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/crypto/eod/standardized` does not have a charting function" ) -def providers_fmp_major_indices_price_standardized(**kwargs): +def providers_fmp_major_indices_price_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/major/indices/price/standardized` does not have a charting function" ) -def providers_fmp_forex_eod_standardized(**kwargs): +def providers_fmp_forex_eod_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/forex/eod/standardized` does not have a charting function" ) -def providers_polygon_stock_eod_standardized(**kwargs): +def providers_polygon_stock_eod_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/stock/eod/standardized` does not have a charting function" ) -def providers_polygon_stock_news_standardized(**kwargs): +def providers_polygon_stock_news_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/stock/news/standardized` does not have a charting function" ) -def providers_polygon_balance_sheet_standardized(**kwargs): +def providers_polygon_balance_sheet_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/balance/sheet/standardized` does not have a charting function" ) -def providers_polygon_income_statement_standardized(**kwargs): +def providers_polygon_income_statement_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/income/statement/standardized` does not have a charting function" ) -def providers_polygon_cash_flow_statement_standardized(**kwargs): +def providers_polygon_cash_flow_statement_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/cash/flow/statement/standardized` does not have a charting function" ) -def providers_polygon_stock_price_standardized(**kwargs): +def providers_polygon_stock_price_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/stock/price/standardized` does not have a charting function" ) -def providers_polygon_crypto_price_standardized(**kwargs): +def providers_polygon_crypto_price_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/crypto/price/standardized` does not have a charting function" ) -def providers_polygon_crypto_eod_standardized(**kwargs): +def providers_polygon_crypto_eod_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/crypto/eod/standardized` does not have a charting function" ) -def providers_polygon_major_indices_eod_standardized(**kwargs): +def providers_polygon_major_indices_eod_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/major/indices/eod/standardized` does not have a charting function" ) -def providers_polygon_major_indices_price_standardized(**kwargs): +def providers_polygon_major_indices_price_standardized( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/major/indices/price/standardized` does not have a charting function" ) -def providers_benzinga_global_news_simple(**kwargs): +def providers_benzinga_global_news_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/benzinga/global/news/simple` does not have a charting function" ) -def providers_benzinga_stock_news_simple(**kwargs): +def providers_benzinga_stock_news_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/benzinga/stock/news/simple` does not have a charting function" ) -def providers_fmp_key_executives_simple(**kwargs): +def providers_fmp_key_executives_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/key/executives/simple` does not have a charting function" ) -def providers_fmp_stock_eod_simple(**kwargs): +def providers_fmp_stock_eod_simple(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/stock/eod/simple` does not have a charting function" ) -def providers_fmp_global_news_simple(**kwargs): +def providers_fmp_global_news_simple(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/global/news/simple` does not have a charting function" ) -def providers_fmp_stock_news_simple(**kwargs): +def providers_fmp_stock_news_simple(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/stock/news/simple` does not have a charting function" ) -def providers_fmp_income_statement_simple(**kwargs): +def providers_fmp_income_statement_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/income/statement/simple` does not have a charting function" ) -def providers_fmp_balance_sheet_simple(**kwargs): +def providers_fmp_balance_sheet_simple(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/balance/sheet/simple` does not have a charting function" ) -def providers_fmp_cash_flow_statement_simple(**kwargs): +def providers_fmp_cash_flow_statement_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/cash/flow/statement/simple` does not have a charting function" ) -def providers_fmp_share_statistics_simple(**kwargs): +def providers_fmp_share_statistics_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/share/statistics/simple` does not have a charting function" ) -def providers_fmp_major_indices_eod_simple(**kwargs): +def providers_fmp_major_indices_eod_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/major/indices/eod/simple` does not have a charting function" ) -def providers_fmp_revenue_geographic_simple(**kwargs): +def providers_fmp_revenue_geographic_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/revenue/geographic/simple` does not have a charting function" ) -def providers_fmp_revenue_business_line_simple(**kwargs): +def providers_fmp_revenue_business_line_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/revenue/business/line/simple` does not have a charting function" ) -def providers_fmp_institutional_ownership_simple(**kwargs): +def providers_fmp_institutional_ownership_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/institutional/ownership/simple` does not have a charting function" ) -def providers_fmp_company_overview_simple(**kwargs): +def providers_fmp_company_overview_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/company/overview/simple` does not have a charting function" ) -def providers_fmp_stock_insider_trading_simple(**kwargs): +def providers_fmp_stock_insider_trading_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/stock/insider/trading/simple` does not have a charting function" ) -def providers_fmp_stock_ownership_simple(**kwargs): +def providers_fmp_stock_ownership_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/stock/ownership/simple` does not have a charting function" ) -def providers_fmp_e_s_g_score_simple(**kwargs): +def providers_fmp_e_s_g_score_simple(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/e/s/g/score/simple` does not have a charting function" ) -def providers_fmp_e_s_g_sector_simple(**kwargs): +def providers_fmp_e_s_g_sector_simple(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/e/s/g/sector/simple` does not have a charting function" ) -def providers_fmp_e_s_g_risk_rating_simple(**kwargs): +def providers_fmp_e_s_g_risk_rating_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/e/s/g/risk/rating/simple` does not have a charting function" ) -def providers_fmp_stock_price_simple(**kwargs): +def providers_fmp_stock_price_simple(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/stock/price/simple` does not have a charting function" ) -def providers_fmp_price_target_consensus_simple(**kwargs): +def providers_fmp_price_target_consensus_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/price/target/consensus/simple` does not have a charting function" ) -def providers_fmp_price_target_simple(**kwargs): +def providers_fmp_price_target_simple(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/price/target/simple` does not have a charting function" ) -def providers_fmp_analyst_estimates_simple(**kwargs): +def providers_fmp_analyst_estimates_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/analyst/estimates/simple` does not have a charting function" ) -def providers_fmp_earnings_calendar_simple(**kwargs): +def providers_fmp_earnings_calendar_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/earnings/calendar/simple` does not have a charting function" ) -def providers_fmp_earnings_call_transcript_simple(**kwargs): +def providers_fmp_earnings_call_transcript_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/earnings/call/transcript/simple` does not have a charting function" ) -def providers_fmp_historical_stock_splits_simple(**kwargs): +def providers_fmp_historical_stock_splits_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/historical/stock/splits/simple` does not have a charting function" ) -def providers_fmp_historical_dividends_simple(**kwargs): +def providers_fmp_historical_dividends_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/historical/dividends/simple` does not have a charting function" ) -def providers_fmp_key_metrics_simple(**kwargs): +def providers_fmp_key_metrics_simple(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/key/metrics/simple` does not have a charting function" ) -def providers_fmp_s_e_c_filings_simple(**kwargs): +def providers_fmp_s_e_c_filings_simple(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/s/e/c/filings/simple` does not have a charting function" ) -def providers_fmp_treasury_rates_simple(**kwargs): +def providers_fmp_treasury_rates_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/treasury/rates/simple` does not have a charting function" ) -def providers_fmp_executive_compensation_simple(**kwargs): +def providers_fmp_executive_compensation_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/executive/compensation/simple` does not have a charting function" ) -def providers_fmp_crypto_price_simple(**kwargs): +def providers_fmp_crypto_price_simple(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/crypto/price/simple` does not have a charting function" ) -def providers_fmp_crypto_eod_simple(**kwargs): +def providers_fmp_crypto_eod_simple(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/crypto/eod/simple` does not have a charting function" ) -def providers_fmp_major_indices_price_simple(**kwargs): +def providers_fmp_major_indices_price_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/major/indices/price/simple` does not have a charting function" ) -def providers_fmp_forex_eod_simple(**kwargs): +def providers_fmp_forex_eod_simple(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/fmp/forex/eod/simple` does not have a charting function" ) -def providers_polygon_stock_eod_simple(**kwargs): +def providers_polygon_stock_eod_simple(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/stock/eod/simple` does not have a charting function" ) -def providers_polygon_stock_news_simple(**kwargs): +def providers_polygon_stock_news_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/stock/news/simple` does not have a charting function" ) -def providers_polygon_balance_sheet_simple(**kwargs): +def providers_polygon_balance_sheet_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/balance/sheet/simple` does not have a charting function" ) -def providers_polygon_income_statement_simple(**kwargs): +def providers_polygon_income_statement_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/income/statement/simple` does not have a charting function" ) -def providers_polygon_cash_flow_statement_simple(**kwargs): +def providers_polygon_cash_flow_statement_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/cash/flow/statement/simple` does not have a charting function" ) -def providers_polygon_stock_price_simple(**kwargs): +def providers_polygon_stock_price_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/stock/price/simple` does not have a charting function" ) -def providers_polygon_crypto_price_simple(**kwargs): +def providers_polygon_crypto_price_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/crypto/price/simple` does not have a charting function" ) -def providers_polygon_crypto_eod_simple(**kwargs): +def providers_polygon_crypto_eod_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/crypto/eod/simple` does not have a charting function" ) -def providers_polygon_major_indices_eod_simple(**kwargs): +def providers_polygon_major_indices_eod_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/major/indices/eod/simple` does not have a charting function" ) -def providers_polygon_major_indices_price_simple(**kwargs): +def providers_polygon_major_indices_price_simple( + **kwargs, +) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `providers/polygon/major/indices/price/simple` does not have a charting function" ) -def qa_normality(**kwargs): +def qa_normality(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `qa/normality` does not have a charting function" ) -def qa_capm(**kwargs): +def qa_capm(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/capm` does not have a charting function") -def qa_qqplot(**kwargs): +def qa_qqplot(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/qqplot` does not have a charting function") -def qa_om(**kwargs): +def qa_om(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/om` does not have a charting function") -def qa_kurtosis(**kwargs): +def qa_kurtosis(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/kurtosis` does not have a charting function") -def qa_pick(**kwargs): +def qa_pick(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/pick` does not have a charting function") -def qa_spread(**kwargs): +def qa_spread(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/spread` does not have a charting function") -def qa_rolling(**kwargs): +def qa_rolling(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/rolling` does not have a charting function") -def qa_var(**kwargs): +def qa_var(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/var` does not have a charting function") -def qa_line(**kwargs): +def qa_line(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/line` does not have a charting function") -def qa_hist(**kwargs): +def qa_hist(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/hist` does not have a charting function") -def qa_unitroot(**kwargs): +def qa_unitroot(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/unitroot` does not have a charting function") -def qa_beta(**kwargs): +def qa_beta(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/beta` does not have a charting function") -def qa_sh(**kwargs): +def qa_sh(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/sh` does not have a charting function") -def qa_so(**kwargs): +def qa_so(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/so` does not have a charting function") -def qa_cusum(**kwargs): +def qa_cusum(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/cusum` does not have a charting function") -def qa_raw(**kwargs): +def qa_raw(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/raw` does not have a charting function") -def qa_cdf(**kwargs): +def qa_cdf(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/cdf` does not have a charting function") -def qa_decompose(**kwargs): +def qa_decompose(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `qa/decompose` does not have a charting function" ) -def qa_skew(**kwargs): +def qa_skew(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/skew` does not have a charting function") -def qa_quantile(**kwargs): +def qa_quantile(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/quantile` does not have a charting function") -def qa_bw(**kwargs): +def qa_bw(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/bw` does not have a charting function") -def qa_es(**kwargs): +def qa_es(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/es` does not have a charting function") -def qa_acf(**kwargs): +def qa_acf(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/acf` does not have a charting function") -def qa_summary(**kwargs): +def qa_summary(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `qa/summary` does not have a charting function") -def stocks_fa_balance(**kwargs): +def stocks_fa_balance(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/balance` does not have a charting function" ) -def stocks_fa_cash(**kwargs): +def stocks_fa_cash(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/cash` does not have a charting function" ) -def stocks_fa_comp(**kwargs): +def stocks_fa_comp(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/comp` does not have a charting function" ) -def stocks_fa_earning(**kwargs): +def stocks_fa_earning(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/earning` does not have a charting function" ) -def stocks_fa_emp(**kwargs): +def stocks_fa_emp(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/emp` does not have a charting function" ) -def stocks_fa_est(**kwargs): +def stocks_fa_est(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/est` does not have a charting function" ) -def stocks_fa_income(**kwargs): +def stocks_fa_income(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/income` does not have a charting function" ) -def stocks_fa_ins(**kwargs): +def stocks_fa_ins(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/ins` does not have a charting function" ) -def stocks_fa_metrics(**kwargs): +def stocks_fa_metrics(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/metrics` does not have a charting function" ) -def stocks_fa_mgmt(**kwargs): +def stocks_fa_mgmt(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/mgmt` does not have a charting function" ) -def stocks_fa_overview(**kwargs): +def stocks_fa_overview(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/overview` does not have a charting function" ) -def stocks_fa_own(**kwargs): +def stocks_fa_own(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/own` does not have a charting function" ) -def stocks_fa_pta(**kwargs): +def stocks_fa_pta(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/pta` does not have a charting function" ) -def stocks_fa_pt(**kwargs): +def stocks_fa_pt(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/pt` does not have a charting function" ) -def stocks_fa_revgeo(**kwargs): +def stocks_fa_revgeo(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/revgeo` does not have a charting function" ) -def stocks_fa_revseg(**kwargs): +def stocks_fa_revseg(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/revseg` does not have a charting function" ) -def stocks_fa_shrs(**kwargs): +def stocks_fa_shrs(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/shrs` does not have a charting function" ) -def stocks_fa_shares(**kwargs): +def stocks_fa_shares(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/shares` does not have a charting function" ) -def stocks_fa_transcript(**kwargs): +def stocks_fa_transcript(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/transcript` does not have a charting function" ) -def stocks_fa_split(**kwargs): +def stocks_fa_split(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/split` does not have a charting function" ) -def stocks_fa_cal(**kwargs): +def stocks_fa_cal(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/cal` does not have a charting function" ) -def stocks_fa_customer(**kwargs): +def stocks_fa_customer(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/customer` does not have a charting function" ) -def stocks_fa_divs(**kwargs): +def stocks_fa_divs(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/divs` does not have a charting function" ) -def stocks_fa_dcfc(**kwargs): +def stocks_fa_dcfc(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/dcfc` does not have a charting function" ) -def stocks_fa_dupont(**kwargs): +def stocks_fa_dupont(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/dupont` does not have a charting function" ) -def stocks_fa_enterprise(**kwargs): +def stocks_fa_enterprise(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/enterprise` does not have a charting function" ) -def stocks_fa_epsfc(**kwargs): +def stocks_fa_epsfc(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/epsfc` does not have a charting function" ) -def stocks_fa_analysis(**kwargs): +def stocks_fa_analysis(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/analysis` does not have a charting function" ) -def stocks_fa_fama_coe(**kwargs): +def stocks_fa_fama_coe(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/fama/coe` does not have a charting function" ) -def stocks_fa_fama_raw(**kwargs): +def stocks_fa_fama_raw(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/fama/raw` does not have a charting function" ) -def stocks_fa_fraud(**kwargs): +def stocks_fa_fraud(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/fraud` does not have a charting function" ) -def stocks_fa_growth(**kwargs): +def stocks_fa_growth(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/growth` does not have a charting function" ) -def stocks_fa_historical_5(**kwargs): +def stocks_fa_historical_5(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/historical/5` does not have a charting function" ) -def stocks_fa_key(**kwargs): +def stocks_fa_key(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/key` does not have a charting function" ) -def stocks_fa_mktcap(**kwargs): +def stocks_fa_mktcap(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/mktcap` does not have a charting function" ) -def stocks_fa_news(**kwargs): +def stocks_fa_news(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/news` does not have a charting function" ) -def stocks_fa_rating(**kwargs): +def stocks_fa_rating(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/rating` does not have a charting function" ) -def stocks_fa_ratios(**kwargs): +def stocks_fa_ratios(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/ratios` does not have a charting function" ) -def stocks_fa_revfc(**kwargs): +def stocks_fa_revfc(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/revfc` does not have a charting function" ) -def stocks_fa_rot(**kwargs): +def stocks_fa_rot(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/rot` does not have a charting function" ) -def stocks_fa_score(**kwargs): +def stocks_fa_score(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/score` does not have a charting function" ) -def stocks_fa_sec(**kwargs): +def stocks_fa_sec(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/sec` does not have a charting function" ) -def stocks_fa_supplier(**kwargs): +def stocks_fa_supplier(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/fa/supplier` does not have a charting function" ) -def stocks_ca_get(**kwargs): +def stocks_ca_get(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ca/get` does not have a charting function" ) -def stocks_ca_balance(**kwargs): +def stocks_ca_balance(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ca/balance` does not have a charting function" ) -def stocks_ca_cashflow(**kwargs): +def stocks_ca_cashflow(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ca/cashflow` does not have a charting function" ) -def stocks_ca_hcorr(**kwargs): +def stocks_ca_hcorr(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ca/hcorr` does not have a charting function" ) -def stocks_ca_hist(**kwargs): +def stocks_ca_hist(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ca/hist` does not have a charting function" ) -def stocks_ca_income(**kwargs): +def stocks_ca_income(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ca/income` does not have a charting function" ) -def stocks_ca_scorr(**kwargs): +def stocks_ca_scorr(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ca/scorr` does not have a charting function" ) -def stocks_ca_screener(**kwargs): +def stocks_ca_screener(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ca/screener` does not have a charting function" ) -def stocks_ca_sentiment(**kwargs): +def stocks_ca_sentiment(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ca/sentiment` does not have a charting function" ) -def stocks_ca_similar(**kwargs): +def stocks_ca_similar(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ca/similar` does not have a charting function" ) -def stocks_ca_volume(**kwargs): +def stocks_ca_volume(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ca/volume` does not have a charting function" ) -def stocks_dd_sec(**kwargs): +def stocks_dd_sec(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/dd/sec` does not have a charting function" ) -def stocks_dps_psi(**kwargs): +def stocks_dps_psi(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/dps/psi` does not have a charting function" ) -def stocks_dps_ctb(**kwargs): +def stocks_dps_ctb(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/dps/ctb` does not have a charting function" ) -def stocks_dps_dpotc(**kwargs): +def stocks_dps_dpotc(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/dps/dpotc` does not have a charting function" ) -def stocks_dps_ftd(**kwargs): +def stocks_dps_ftd(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/dps/ftd` does not have a charting function" ) -def stocks_dps_hsi(**kwargs): +def stocks_dps_hsi(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/dps/hsi` does not have a charting function" ) -def stocks_dps_pos(**kwargs): +def stocks_dps_pos(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/dps/pos` does not have a charting function" ) -def stocks_dps_prom(**kwargs): +def stocks_dps_prom(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/dps/prom` does not have a charting function" ) -def stocks_dps_psi_q(**kwargs): +def stocks_dps_psi_q(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/dps/psi/q` does not have a charting function" ) -def stocks_dps_psi_sg(**kwargs): +def stocks_dps_psi_sg(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/dps/psi/sg` does not have a charting function" ) -def stocks_dps_shorted(**kwargs): +def stocks_dps_shorted(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/dps/shorted` does not have a charting function" ) -def stocks_dps_sidtc(**kwargs): +def stocks_dps_sidtc(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/dps/sidtc` does not have a charting function" ) -def stocks_dps_spos(**kwargs): +def stocks_dps_spos(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/dps/spos` does not have a charting function" ) -def stocks_disc_active(**kwargs): +def stocks_disc_active(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/active` does not have a charting function" ) -def stocks_disc_arkord(**kwargs): +def stocks_disc_arkord(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/arkord` does not have a charting function" ) -def stocks_disc_asc(**kwargs): +def stocks_disc_asc(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/asc` does not have a charting function" ) -def stocks_disc_dividends(**kwargs): +def stocks_disc_dividends(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/dividends` does not have a charting function" ) -def stocks_disc_filings(**kwargs): +def stocks_disc_filings(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/filings` does not have a charting function" ) -def stocks_disc_fipo(**kwargs): +def stocks_disc_fipo(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/fipo` does not have a charting function" ) -def stocks_disc_gainers(**kwargs): +def stocks_disc_gainers(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/gainers` does not have a charting function" ) -def stocks_disc_gtech(**kwargs): +def stocks_disc_gtech(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/gtech` does not have a charting function" ) -def stocks_disc_hotpenny(**kwargs): +def stocks_disc_hotpenny(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/hotpenny` does not have a charting function" ) -def stocks_disc_ipo(**kwargs): +def stocks_disc_ipo(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/ipo` does not have a charting function" ) -def stocks_disc_losers(**kwargs): +def stocks_disc_losers(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/losers` does not have a charting function" ) -def stocks_disc_lowfloat(**kwargs): +def stocks_disc_lowfloat(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/lowfloat` does not have a charting function" ) -def stocks_disc_pipo(**kwargs): +def stocks_disc_pipo(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/pipo` does not have a charting function" ) -def stocks_disc_rtat(**kwargs): +def stocks_disc_rtat(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/rtat` does not have a charting function" ) -def stocks_disc_trending(**kwargs): +def stocks_disc_trending(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/trending` does not have a charting function" ) -def stocks_disc_ugs(**kwargs): +def stocks_disc_ugs(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/ugs` does not have a charting function" ) -def stocks_disc_ulc(**kwargs): +def stocks_disc_ulc(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/ulc` does not have a charting function" ) -def stocks_disc_upcoming(**kwargs): +def stocks_disc_upcoming(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/disc/upcoming` does not have a charting function" ) -def stocks_gov_contracts(**kwargs): +def stocks_gov_contracts(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/gov/contracts` does not have a charting function" ) -def stocks_gov_government_trading(**kwargs): +def stocks_gov_government_trading(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/gov/government/trading` does not have a charting function" ) -def stocks_gov_gtrades(**kwargs): +def stocks_gov_gtrades(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/gov/gtrades` does not have a charting function" ) -def stocks_gov_histcont(**kwargs): +def stocks_gov_histcont(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/gov/histcont` does not have a charting function" ) -def stocks_gov_lastcontracts(**kwargs): +def stocks_gov_lastcontracts(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/gov/lastcontracts` does not have a charting function" ) -def stocks_gov_lasttrades(**kwargs): +def stocks_gov_lasttrades(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/gov/lasttrades` does not have a charting function" ) -def stocks_gov_lobbying(**kwargs): +def stocks_gov_lobbying(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/gov/lobbying` does not have a charting function" ) -def stocks_gov_qtrcontracts(**kwargs): +def stocks_gov_qtrcontracts(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/gov/qtrcontracts` does not have a charting function" ) -def stocks_gov_topbuys(**kwargs): +def stocks_gov_topbuys(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/gov/topbuys` does not have a charting function" ) -def stocks_gov_toplobbying(**kwargs): +def stocks_gov_toplobbying(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/gov/toplobbying` does not have a charting function" ) -def stocks_gov_topsells(**kwargs): +def stocks_gov_topsells(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/gov/topsells` does not have a charting function" ) -def stocks_ins_act(**kwargs): +def stocks_ins_act(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/act` does not have a charting function" ) -def stocks_ins_blcp(**kwargs): +def stocks_ins_blcp(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/blcp` does not have a charting function" ) -def stocks_ins_blcs(**kwargs): +def stocks_ins_blcs(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/blcs` does not have a charting function" ) -def stocks_ins_blip(**kwargs): +def stocks_ins_blip(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/blip` does not have a charting function" ) -def stocks_ins_blis(**kwargs): +def stocks_ins_blis(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/blis` does not have a charting function" ) -def stocks_ins_blop(**kwargs): +def stocks_ins_blop(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/blop` does not have a charting function" ) -def stocks_ins_blos(**kwargs): +def stocks_ins_blos(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/blos` does not have a charting function" ) -def stocks_ins_filter(**kwargs): +def stocks_ins_filter(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/filter` does not have a charting function" ) -def stocks_ins_lcb(**kwargs): +def stocks_ins_lcb(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/lcb` does not have a charting function" ) -def stocks_ins_lins(**kwargs): +def stocks_ins_lins(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/lins` does not have a charting function" ) -def stocks_ins_lip(**kwargs): +def stocks_ins_lip(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/lip` does not have a charting function" ) -def stocks_ins_lis(**kwargs): +def stocks_ins_lis(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/lis` does not have a charting function" ) -def stocks_ins_lit(**kwargs): +def stocks_ins_lit(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/lit` does not have a charting function" ) -def stocks_ins_lpsb(**kwargs): +def stocks_ins_lpsb(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/lpsb` does not have a charting function" ) -def stocks_ins_print_insider_data(**kwargs): +def stocks_ins_print_insider_data(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/print/insider/data` does not have a charting function" ) -def stocks_ins_stats(**kwargs): +def stocks_ins_stats(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/ins/stats` does not have a charting function" ) -def stocks_options_chains(**kwargs): +def stocks_options_chains(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/options/chains` does not have a charting function" ) -def stocks_options_dte(**kwargs): +def stocks_options_dte(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/options/dte` does not have a charting function" ) -def stocks_options_eodchain(**kwargs): +def stocks_options_eodchain(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/options/eodchain` does not have a charting function" ) -def stocks_options_expirations(**kwargs): +def stocks_options_expirations(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/options/expirations` does not have a charting function" ) -def stocks_options_grhist(**kwargs): +def stocks_options_grhist(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/options/grhist` does not have a charting function" ) -def stocks_options_hist(**kwargs): +def stocks_options_hist(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/options/hist` does not have a charting function" ) -def stocks_options_info(**kwargs): +def stocks_options_info(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/options/info` does not have a charting function" ) -def stocks_options_last_price(**kwargs): +def stocks_options_last_price(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/options/last/price` does not have a charting function" ) -def stocks_options_oi(**kwargs): +def stocks_options_oi(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/options/oi` does not have a charting function" ) -def stocks_options_pcr(**kwargs): +def stocks_options_pcr(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/options/pcr` does not have a charting function" ) -def stocks_options_price(**kwargs): +def stocks_options_price(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/options/price` does not have a charting function" ) -def stocks_options_unu(**kwargs): +def stocks_options_unu(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/options/unu` does not have a charting function" ) -def stocks_options_voi(**kwargs): +def stocks_options_voi(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/options/voi` does not have a charting function" ) -def stocks_options_vol(**kwargs): +def stocks_options_vol(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/options/vol` does not have a charting function" ) -def stocks_options_vsurf(**kwargs): +def stocks_options_vsurf(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/options/vsurf` does not have a charting function" ) -def stocks_load(**kwargs): +def stocks_load(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: def handle_indicators(ma): k = {} if ma: @@ -1674,25 +1834,35 @@ def stocks_load(**kwargs): symbol=f"{symbol} historical data", prepost=prepost, ) + content = fig.show(external=True).to_plotly_json() - return fig.show(external=True).to_plotly_json() + return fig, content -def stocks_news(**kwargs): - raise NotImplementedError("Command `stocks/news` does not have a charting function") +def stocks_news(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: + data = basemodel_to_df( + kwargs["command_output_item"], index=kwargs.get("index", "date") + ) + standard_params = kwargs["standard_params"].__dict__ + columnwidth = standard_params.get("columnwidth", None) + + tbl_fig = OpenBBFigureTable(tabular_data=data, columnwidth=columnwidth) + content = tbl_fig.to_table().show(external=True).to_plotly_json() + + return tbl_fig, content -def stocks_tob(**kwargs): +def stocks_tob(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `stocks/tob` does not have a charting function") -def stocks_quote(**kwargs): +def stocks_quote(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/quote` does not have a charting function" ) -def stocks_search(**kwargs): +def stocks_search(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError( "Command `stocks/search` does not have a charting function" ) @@ -1700,7 +1870,7 @@ def stocks_search(**kwargs): def _ta_ma(ma_type: str, **kwargs): data = basemodel_to_df(kwargs["data"], index=kwargs.get("index", "date")) - window = kwargs.get("window", 50) + window = kwargs.get("windowstocks_load", 50) offset = kwargs.get("offset", 0) symbol = kwargs.get("symbol", "") @@ -1712,48 +1882,49 @@ def _ta_ma(ma_type: str, **kwargs): False, volume=False, ) + content = fig.show(external=True).to_plotly_json() - return fig.show(external=True).to_plotly_json() + return fig, content -def ta_atr(**kwargs): +def ta_atr(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/atr` does not have a charting function") -def ta_fib(**kwargs): +def ta_fib(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/fib` does not have a charting function") -def ta_obv(**kwargs): +def ta_obv(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/obv` does not have a charting function") -def ta_fisher(**kwargs): +def ta_fisher(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/fisher` does not have a charting function") -def ta_adosc(**kwargs): +def ta_adosc(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/adosc` does not have a charting function") -def ta_tv(**kwargs): +def ta_tv(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/tv` does not have a charting function") -def ta_bbands(**kwargs): +def ta_bbands(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/bbands` does not have a charting function") -def ta_multi(**kwargs): +def ta_multi(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/multi` does not have a charting function") -def ta_zlma(**kwargs): +def ta_zlma(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: ma_type = "zlma" return _ta_ma(ma_type, **kwargs) -def ta_aroon(**kwargs): +def ta_aroon(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: data = basemodel_to_df(kwargs["data"], index=kwargs.get("index", "date")) length = kwargs.get("length", 25) scalar = kwargs.get("scalar", 100) @@ -1767,28 +1938,29 @@ def ta_aroon(**kwargs): False, volume=False, ) + content = fig.show(external=True).to_plotly_json() - return fig.show(external=True).to_plotly_json() + return fig, content -def ta_sma(**kwargs): +def ta_sma(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: ma_type = "sma" return _ta_ma(ma_type, **kwargs) -def ta_demark(**kwargs): +def ta_demark(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/demark` does not have a charting function") -def ta_vwap(**kwargs): +def ta_vwap(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/vwap` does not have a charting function") -def ta_recom(**kwargs): +def ta_recom(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/recom` does not have a charting function") -def ta_macd(**kwargs): +def ta_macd(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: data = basemodel_to_df(kwargs["data"], index=kwargs.get("index", "date")) fast = kwargs.get("fast", 12) slow = kwargs.get("slow", 26) @@ -1803,32 +1975,33 @@ def ta_macd(**kwargs): False, volume=False, ) + content = fig.show(external=True).to_plotly_json() - return fig.show(external=True).to_plotly_json() + return fig, content -def ta_hma(**kwargs): +def ta_hma(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: ma_type = "hma" return _ta_ma(ma_type, **kwargs) -def ta_donchian(**kwargs): +def ta_donchian(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/donchian` does not have a charting function") -def ta_ichimoku(**kwargs): +def ta_ichimoku(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/ichimoku` does not have a charting function") -def ta_clenow(**kwargs): +def ta_clenow(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/clenow` does not have a charting function") -def ta_ad(**kwargs): +def ta_ad(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/ad` does not have a charting function") -def ta_adx(**kwargs): +def ta_adx(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: data = basemodel_to_df(kwargs["data"], index=kwargs.get("index", "date")) length = kwargs.get("length", 14) scalar = kwargs.get("scalar", 100.0) @@ -1843,20 +2016,21 @@ def ta_adx(**kwargs): False, volume=False, ) + content = fig.show(external=True).to_plotly_json() - return fig.show(external=True).to_plotly_json() + return fig, content -def ta_wma(**kwargs): +def ta_wma(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: ma_type = "wma" return _ta_ma(ma_type, **kwargs) -def ta_cci(**kwargs): +def ta_cci(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/cci` does not have a charting function") -def ta_rsi(**kwargs): +def ta_rsi(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: data = basemodel_to_df(kwargs["data"], index=kwargs.get("index", "date")) window = kwargs.get("window", 14) scalar = kwargs.get("scalar", 100.0) @@ -1871,34 +2045,76 @@ def ta_rsi(**kwargs): False, volume=False, ) + content = fig.show(external=True).to_plotly_json() - return fig.show(external=True).to_plotly_json() + return fig, content -def ta_summary(**kwargs): +def ta_summary(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/summary` does not have a charting function") -def ta_stoch(**kwargs): +def ta_stoch(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/stoch` does not have a charting function") -def ta_rsp(**kwargs): +def ta_rsp(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/rsp` does not have a charting function") -def ta_kc(**kwargs): +def ta_kc(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/kc` does not have a charting function") -def ta_cg(**kwargs): +def ta_cg(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/cg` does not have a charting function") -def ta_cones(**kwargs): +def ta_cones(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: raise NotImplementedError("Command `ta/cones` does not have a charting function") -def ta_ema(**kwargs): +def ta_ema(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: ma_type = "ema" return _ta_ma(ma_type, **kwargs) + + +def forex_pairs(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: + raise NotImplementedError("Command `forex/pairs` does not have a charting function") + + +def economy_const(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: + raise NotImplementedError( + "Command `economy/const` does not have a charting function" + ) + + +def economy_risk(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: + raise NotImplementedError( + "Command `economy/risk` does not have a charting function" + ) + + +def stocks_fa_comsplit(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: + raise NotImplementedError( + "Command `stocks/fa/comsplit` does not have a charting function" + ) + + +def stocks_ins_filt(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: + raise NotImplementedError( + "Command `stocks/ins/filt` does not have a charting function" + ) + + +def stocks_multiples(**kwargs) -> Tuple[OpenBBFigure, Dict[str, Any]]: + data = basemodel_to_df( + kwargs["command_output_item"], index=kwargs.get("index", "date") + ) + standard_params = kwargs["standard_params"].__dict__ + columnwidth = standard_params.get("columnwidth", None) + + tbl_fig = OpenBBFigureTable(tabular_data=data, columnwidth=columnwidth) + content = tbl_fig.to_table().show(external=True).to_plotly_json() + + return tbl_fig, content diff --git a/openbb_sdk/extensions/charting/openbb_charting/core/chart_style.py b/openbb_sdk/extensions/charting/openbb_charting/core/chart_style.py index 31da7b825e4..e90491b95af 100644 --- a/openbb_sdk/extensions/charting/openbb_charting/core/chart_style.py +++ b/openbb_sdk/extensions/charting/openbb_charting/core/chart_style.py @@ -30,7 +30,7 @@ class ChartStyle: """ STYLES_REPO = Path(__file__).parent.parent / "styles" - user_styles_directory = STYLES_REPO + user_styles_directory: Path = STYLES_REPO plt_styles_available: Dict[str, Path] = {} plt_style: str = "dark" @@ -58,7 +58,7 @@ class ChartStyle: def __init__( self, plt_style: Optional[str] = "", - user_styles_directory: Optional[Union[str, Path]] = None, + user_styles_directory: Optional[Path] = None, ): """Initialize the class. @@ -137,7 +137,7 @@ class ChartStyle: for attr, ext in zip( ["plt_styles_available", "console_styles_available"], - [".pltstyle", ".richstyle.json"], + [".pltstyle.json", ".richstyle.json"], ): for file in folder.rglob(f"*{ext}"): getattr(self, attr)[file.name.replace(ext, "")] = file diff --git a/openbb_sdk/extensions/charting/openbb_charting/core/openbb_figure.py b/openbb_sdk/extensions/charting/openbb_charting/core/openbb_figure.py index 718bb7d1c3e..fd35d1238f5 100644 --- a/openbb_sdk/extensions/charting/openbb_charting/core/openbb_figure.py +++ b/openbb_sdk/extensions/charting/openbb_charting/core/openbb_figure.py @@ -338,9 +338,7 @@ class OpenBBFigure(go.Figure): for i, (x_i, name_i, color_i) in enumerate(zip(valid_x, name, colors)): if not color_i: color_i = ( # noqa: PLW2901 - self._self._theme.up_color - if i % 2 == 0 - else self._self._theme.down_color + self._theme.up_color if i % 2 == 0 else self._theme.down_color ) res_mean, res_std = np.mean(x_i), np.std(x_i) @@ -386,7 +384,7 @@ class OpenBBFigure(go.Figure): name=name_i if len(name) < 2 else name[1], mode="markers", marker=dict( - color=self._self._theme.down_color, + color=self._theme.down_color, symbol="line-ns-open", size=8, ), @@ -1381,7 +1379,7 @@ class OpenBBFigure(go.Figure): height = kwargs.pop("height", len(data.index) * 28 + 25) width = kwargs.pop("width", sum(columnwidth) * 8.7) - fig: OpenBBFigure = cls() + fig = OpenBBFigure(**kwargs) fig.add_table( header=dict(values=header_values), cells=dict( diff --git a/openbb_sdk/extensions/charting/openbb_charting/core/openbb_figure_table.py b/openbb_sdk/extensions/charting/openbb_charting/core/openbb_figure_table.py new file mode 100644 index 00000000000..b0d131a7251 --- /dev/null +++ b/openbb_sdk/extensions/charting/openbb_charting/core/openbb_figure_table.py @@ -0,0 +1,95 @@ +from typing import List, Optional, Union + +import pandas as pd + +from openbb_sdk.extensions.charting.openbb_charting.core.openbb_figure import ( + OpenBBFigure, +) + + +class OpenBBFigureTable(OpenBBFigure): + def __init__( + self, + tabular_data: pd.DataFrame, + columnwidth: Optional[List[Union[int, float]]] = None, + ): + super().__init__() + self._tabular_data = tabular_data + self._columnwidth = columnwidth + + def show( + self, + show_index: Optional[bool] = True, + index_name: Optional[str] = "", + headers: Optional[Union[List[str], pd.Index]] = None, + title: Optional[str] = "", + source: Optional[str] = "", + external: Optional[bool] = False, + ) -> Optional["OpenBBFigureTable"]: + if external: + return self + + if not self._backend.isatty: + tmp_fig = self.to_table() + tmp_fig.show() + else: + # Make a copy of the dataframe to avoid SettingWithCopyWarning + df = self._tabular_data.copy() + + show_index = not isinstance(df.index, pd.RangeIndex) and show_index + # convert non-str that are not timestamp or int into str + # eg) praw.models.reddit.subreddit.Subreddit + for col in df.columns: + try: + if not any( + isinstance(df[col].iloc[x], pd.Timestamp) + for x in range(min(10, len(df))) + ): + df[col] = pd.to_numeric(df[col], errors="ignore") + except (ValueError, TypeError): + df[col] = df[col].astype(str) + + def _get_headers(_headers: Union[List[str], pd.Index]) -> List[str]: + """Check if headers are valid and return them.""" + output = _headers + if isinstance(_headers, pd.Index): + output = list(_headers) + if len(output) != len(df.columns): + raise ( + ValueError( + "Length of headers does not match length of DataFrame" + ) + ) + return output + + df_outgoing = df.copy() + # If headers are provided, use them + if headers is not None: + # We check if headers are valid + df_outgoing.columns = _get_headers(headers) + + if show_index and index_name not in df_outgoing.columns: + # If index name is provided, we use it + df_outgoing.index.name = index_name or "Index" + df_outgoing = df_outgoing.reset_index() + + for col in df_outgoing.columns: + if col == "": + df_outgoing = df_outgoing.rename(columns={col: " "}) + + self._backend.send_table( + df_table=df_outgoing, + title=title, + source=source, # type: ignore + theme=self._charting_settings.table_style, + ) + + def to_table( + self, + print_index: bool = True, + **kwargs, + ) -> "OpenBBFigure": + data = self._tabular_data + columnwidth = self._columnwidth + + return super().to_table(data, columnwidth, print_index, **kwargs) diff --git a/openbb_sdk/extensions/charting/openbb_charting/core/to_plotly_json.py b/openbb_sdk/extensions/charting/openbb_charting/core/to_chart.py similarity index 88% rename from openbb_sdk/extensions/charting/openbb_charting/core/to_plotly_json.py rename to openbb_sdk/extensions/charting/openbb_charting/core/to_chart.py index 2a5745ae08b..f707a6aaab2 100644 --- a/openbb_sdk/extensions/charting/openbb_charting/core/to_plotly_json.py +++ b/openbb_sdk/extensions/charting/openbb_charting/core/to_chart.py @@ -1,12 +1,13 @@ -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Optional, Tuple, Union import pandas as pd +from openbb_charting.core.openbb_figure import OpenBBFigure from openbb_charting.core.plotly_ta.data_classes import ChartIndicators from openbb_charting.core.plotly_ta.ta_class import PlotlyTA from openbb_core.app.model.charts.charting_settings import ChartingSettings -def to_plotly_json( +def to_chart( charting_settings: ChartingSettings, data: Union[pd.DataFrame, pd.Series], indicators: Optional[Union[ChartIndicators, Dict[str, Dict[str, Any]]]] = None, @@ -15,7 +16,7 @@ def to_plotly_json( volume: bool = True, prepost: bool = False, volume_ticks_x: int = 7, -) -> str: +) -> Tuple[OpenBBFigure, Dict[str, Any]]: """ Returns the plotly json representation of the chart. This function is used so it can be called at the module level and used out of the box, @@ -57,5 +58,6 @@ def to_plotly_json( prepost=prepost, volume_ticks_x=volume_ticks_x, ) + content = fig.show(external=True).to_plotly_json() - return fig.show(external=True).to_plotly_json() + return fig, content diff --git a/openbb_sdk/extensions/charting/openbb_charting/styles/default/dark.pltstyle b/openbb_sdk/extensions/charting/openbb_charting/styles/default/dark.pltstyle.json similarity index 100% rename from openbb_sdk/extensions/charting/openbb_charting/styles/default/dark.pltstyle rename to openbb_sdk/extensions/charting/openbb_charting/styles/default/dark.pltstyle.json diff --git a/openbb_sdk/extensions/charting/openbb_charting/styles/default/light.pltstyle b/openbb_sdk/extensions/charting/openbb_charting/styles/default/light.pltstyle.json similarity index 100% rename from openbb_sdk/extensions/charting/openbb_charting/styles/default/light.pltstyle rename to openbb_sdk/extensions/charting/openbb_charting/styles/default/light.pltstyle.json diff --git a/openbb_sdk/extensions/charting/openbb_charting/styles/default/tables.pltstyle.json b/openbb_sdk/extensions/charting/openbb_charting/styles/default/tables.pltstyle.json new file mode 100644 index 00000000000..69f80ef4d88 --- /dev/null +++ b/openbb_sdk/extensions/charting/openbb_charting/styles/default/tables.pltstyle.json @@ -0,0 +1,102 @@ +{ + "data": { + "candlestick": [ + { + "decreasing": { + "fillcolor": "#e4003a", + "line": { + "color": "#e4003a" + } + }, + "increasing": { + "fillcolor": "#00ACFF", + "line": { + "color": "#00ACFF" + } + }, + "type": "candlestick" + } + ] + }, + "layout": { + "annotationdefaults": { + "showarrow": false + }, + "autotypenumbers": "strict", + "colorway": [ + "#ffed00", + "#ef7d00", + "#e4003a", + "#c13246", + "#822661", + "#48277c", + "#005ca9", + "#00aaff", + "#9b30d9", + "#af005f", + "#5f00af", + "#af87ff" + ], + "dragmode": "pan", + "font": { + "family": "Fira Code", + "size": 18 + }, + "hoverlabel": { + "align": "left" + }, + "mapbox": { + "style": "light" + }, + "hovermode": "x", + "legend": { + "bgcolor": "rgba(0, 0, 0, 0)", + "x": 0.01, + "xanchor": "left", + "y": 0.99, + "yanchor": "top", + "font": { + "size": 15 + } + }, + "paper_bgcolor": "white", + "plot_bgcolor": "white", + "xaxis": { + "automargin": true, + "autorange": true, + "rangeslider": { + "visible": false + }, + "showgrid": true, + "showline": true, + "tickfont": { + "size": 14 + }, + "zeroline": false, + "tick0": 1, + "title": { + "standoff": 20 + }, + "linecolor": "#F5EFF3", + "mirror": true, + "ticks": "outside" + }, + "yaxis": { + "anchor": "x", + "automargin": true, + "fixedrange": false, + "zeroline": false, + "showgrid": true, + "showline": true, + "side": "right", + "tick0": 0.5, + "title": { + "standoff": 20 + }, + "gridcolor": "#283442", + "linecolor": "#F5EFF3", + "mirror": true, + "ticks": "outside" + } + } +} diff --git a/openbb_sdk/sdk/core/openbb_core/app/model/error/__init__.py b/openbb_sdk/extensions/charting/tests/.gitkeep similarity index 100% rename from openbb_sdk/sdk/core/openbb_core/app/model/error/__init__.py rename to openbb_sdk/extensions/charting/tests/.gitkeep diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/__init__.py b/openbb_sdk/extensions/crypto/tests/.gitkeep similarity index 100% rename from openbb_sdk/sdk/provider/openbb_provider/models/__init__.py rename to openbb_sdk/extensions/crypto/tests/.gitkeep diff --git a/openbb_sdk/extensions/economy/openbb_economy/economy_router.py b/openbb_sdk/extensions/economy/openbb_economy/economy_router.py index e0483e96053..e4dc585a654 100644 --- a/openbb_sdk/extensions/economy/openbb_economy/economy_router.py +++ b/openbb_sdk/extensions/economy/openbb_economy/economy_router.py @@ -13,7 +13,7 @@ from pydantic import BaseModel router = Router(prefix="") -# Remove this once the placeholders are replaced with real commands +# Remove this once the placeholders are replaced with real commands. # pylint: disable=unused-argument @@ -33,11 +33,10 @@ def const( standard_params: StandardParams, extra_params: ExtraParams, ) -> CommandOutput[BaseModel]: - """""" + """Get the constituents of an index.""" return CommandOutput(results=Query(**locals()).execute()) -# pylint: disable=too-many-arguments @router.command(model="CPI") def cpi( cc: CommandContext, @@ -57,7 +56,7 @@ def cpi_options( return CommandOutput(results=Empty()) -@router.command(model="MajorIndicesPrice") +@router.command(model="MajorIndicesEOD") def index( cc: CommandContext, provider_choices: ProviderChoices, diff --git a/openbb_sdk/extensions/economy/tests/.gitkeep b/openbb_sdk/extensions/economy/tests/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/extensions/fixedincome/tests/.gitkeep b/openbb_sdk/extensions/fixedincome/tests/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/extensions/forex/tests/.gitkeep b/openbb_sdk/extensions/forex/tests/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/extensions/news/tests/.gitkeep b/openbb_sdk/extensions/news/tests/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/extensions/qa/tests/.gitkeep b/openbb_sdk/extensions/qa/tests/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/extensions/stocks/openbb_stocks/ca/ca_router.py b/openbb_sdk/extensions/stocks/openbb_stocks/ca/ca_router.py index 102a01e8d36..51f2ed451c4 100644 --- a/openbb_sdk/extensions/stocks/openbb_stocks/ca/ca_router.py +++ b/openbb_sdk/extensions/stocks/openbb_stocks/ca/ca_router.py @@ -17,7 +17,7 @@ router = Router(prefix="/ca") @router.command(model="StockPeers") -def get( +def peers( cc: CommandContext, provider_choices: ProviderChoices, standard_params: StandardParams, diff --git a/openbb_sdk/extensions/stocks/openbb_stocks/fa/fa_router.py b/openbb_sdk/extensions/stocks/openbb_stocks/fa/fa_router.py index 8c33126f833..19e3cc5f272 100644 --- a/openbb_sdk/extensions/stocks/openbb_stocks/fa/fa_router.py +++ b/openbb_sdk/extensions/stocks/openbb_stocks/fa/fa_router.py @@ -33,6 +33,17 @@ def balance( return CommandOutput(results=Query(**locals()).execute()) +@router.command(model="BalanceSheetGrowth") +def balance_growth( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> CommandOutput[BaseModel]: + """Balance Sheet Statement Growth.""" + return CommandOutput(results=Query(**locals()).execute()) + + @router.command(model="DividendCalendar") def cal( cc: CommandContext, @@ -55,6 +66,17 @@ def cash( return CommandOutput(results=Query(**locals()).execute()) +@router.command(model="CashFlowStatementGrowth") +def cash_growth( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> CommandOutput[BaseModel]: + """Cash Flow Statement Growth.""" + return CommandOutput(results=Query(**locals()).execute()) + + @router.command(model="ExecutiveCompensation") def comp( cc: CommandContext, @@ -191,6 +213,17 @@ def income( return CommandOutput(results=Query(**locals()).execute()) +@router.command(model="IncomeStatementGrowth") +def income_growth( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> CommandOutput[BaseModel]: + """Income Statement Growth.""" + return CommandOutput(results=Query(**locals()).execute()) + + @router.command(model="StockInsiderTrading") def ins( cc: CommandContext, @@ -202,6 +235,17 @@ def ins( return CommandOutput(results=Query(**locals()).execute()) +@router.command(model="InstitutionalOwnership") +def ins_own( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> CommandOutput[BaseModel]: + """Institutional Ownership.""" + return CommandOutput(results=Query(**locals()).execute()) + + @router.command def key() -> CommandOutput[Empty]: # type: ignore return CommandOutput(results=Empty()) @@ -251,14 +295,14 @@ def overview( return CommandOutput(results=Query(**locals()).execute()) -@router.command(model="InstitutionalOwnership") +@router.command(model="StockOwnership") def own( cc: CommandContext, provider_choices: ProviderChoices, standard_params: StandardParams, extra_params: ExtraParams, ) -> CommandOutput[BaseModel]: - """Institutional Ownership.""" + """Stock Ownership.""" return CommandOutput(results=Query(**locals()).execute()) diff --git a/openbb_sdk/extensions/stocks/tests/.gitkeep b/openbb_sdk/extensions/stocks/tests/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/extensions/ta/tests/.gitkeep b/openbb_sdk/extensions/ta/tests/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/openbb/__init__.py b/openbb_sdk/openbb/__init__.py index 0e24835e31f..0ea5db54139 100644 --- a/openbb_sdk/openbb/__init__.py +++ b/openbb_sdk/openbb/__init__.py @@ -1,14 +1,15 @@ # flake8: noqa +from typing import List from openbb_core.app.static.app_factory import create_app as __create_app sdk = __create_app() obb = sdk -def _rebuild_python_interface() -> None: +def _rebuild_python_interface(*args, lint=True) -> None: """Rebuild the Python SDK.""" from openbb_core.app.static.package_builder import ( # pylint: disable=import-outside-toplevel PackageBuilder, ) - PackageBuilder.build() + PackageBuilder.build(modules=list(args), lint=lint) diff --git a/openbb_sdk/providers/benzinga/openbb_benzinga/__init__.py b/openbb_sdk/providers/benzinga/openbb_benzinga/__init__.py index e69de29bb2d..9b43ab598da 100644 --- a/openbb_sdk/providers/benzinga/openbb_benzinga/__init__.py +++ b/openbb_sdk/providers/benzinga/openbb_benzinga/__init__.py @@ -0,0 +1,17 @@ +"""Benzinga provider module.""" +from openbb_provider.abstract.provider import Provider + +from openbb_benzinga.models.global_news import BenzingaGlobalNewsFetcher +from openbb_benzinga.models.stock_news import BenzingaStockNewsFetcher + +benzinga_provider = Provider( + name="benzinga", + website="https://www.benzinga.com/", + description="""Benzinga is a financial data provider that offers an API + focused on information that moves the market.""", + required_credentials=["api_key"], + fetcher_dict={ + "GlobalNews": BenzingaGlobalNewsFetcher, + "StockNews": BenzingaStockNewsFetcher, + }, +) diff --git a/openbb_sdk/providers/benzinga/openbb_benzinga/models/global_news.py b/openbb_sdk/providers/benzinga/openbb_benzinga/models/global_news.py index 4be137d912a..e2f8400e11c 100644 --- a/openbb_sdk/providers/benzinga/openbb_benzinga/models/global_news.py +++ b/openbb_sdk/providers/benzinga/openbb_benzinga/models/global_news.py @@ -2,108 +2,124 @@ from datetime import datetime -from typing import Dict, List, Optional +from typing import Any, Dict, List, Literal, Optional -from openbb_provider.abstract.data import QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer, get_querystring -from openbb_provider.models.global_news import GlobalNewsData, GlobalNewsQueryParams -from pydantic import Field +from openbb_provider.standard_models.global_news import ( + GlobalNewsData, + GlobalNewsQueryParams, +) +from openbb_provider.utils.helpers import get_querystring +from pydantic import Field, validator -from openbb_benzinga.utils.helpers import BenzingaBaseNewsData, get_data +from openbb_benzinga.utils.helpers import BenzingaImage, get_data -class BenzingaGlobalNewsQueryParams(QueryParams): +class BenzingaGlobalNewsQueryParams(GlobalNewsQueryParams): """Benzinga Global News query. Source: https://docs.benzinga.io/benzinga/newsfeed-v2.html - - Parameter - --------- - page : int (default: 0) - The page of the data to retrieve. - pageSize : int (default: 15) - The number of results to return per page. - displayOutput : str (default: "headline") - The type of data to return. Options are "headline", "summary", "full", "all". - date : Optional[datetime] - The date of the news to retrieve. - dateFrom : Optional[datetime] - The start date of the news to retrieve. - dateTo : Optional[datetime] - The end date of the news to retrieve. - updatedSince : Optional[int] - The number of seconds since the news was updated. - publishedSince : Optional[int] - The number of seconds since the news was published. - sort : Optional[str] - The order in which to sort the news. Options are: - "published_at", "updated_at", "title", "author", "channel", "ticker", "topic", "content_type". - isin : Optional[str] - The ISIN of the news to retrieve. - cusip : Optional[str] - The CUSIP of the news to retrieve. - tickers : Optional[str] - The tickers of the news to retrieve. - channels : Optional[str] - The channels of the news to retrieve. - topics : Optional[str] - The topics of the news to retrieve. - authors : Optional[str] - The authors of the news to retrieve. - content_types : Optional[str] - The content types of the news to retrieve. """ - page: int = Field(default=0) - pageSize: int = Field(default=15) - displayOutput: str = Field(default="headline") - date: Optional[datetime] = None - dateFrom: Optional[datetime] = None - dateTo: Optional[datetime] = None - updatedSince: Optional[int] = None - publishedSince: Optional[int] = None - sort: Optional[str] = None - isin: Optional[str] = None - cusip: Optional[str] = None - tickers: Optional[str] = None - channels: Optional[str] = None - topics: Optional[str] = None - authors: Optional[str] = None - content_types: Optional[str] = None + pageSize: int = Field( + default=15, description="The number of results to return per page." + ) + displayOutput: Literal["headline", "summary", "full", "all"] = Field( + default="headline", description="The type of data to return." + ) + date: Optional[datetime] = Field( + default=None, description="The date of the news to retrieve." + ) + dateFrom: Optional[datetime] = Field( + default=None, description="The start date of the news to retrieve." + ) + dateTo: Optional[datetime] = Field( + default=None, description="The end date of the news to retrieve." + ) + updatedSince: Optional[int] = Field( + default=None, + description="The number of seconds since the news was updated.", + ) + publishedSince: Optional[int] = Field( + default=None, + description="The number of seconds since the news was published.", + ) + sort: Optional[ + Literal[ + "published_at", + "updated_at", + "title", + "author", + "channel", + "ticker", + "topic", + "content_type", + ] + ] = Field( + default=None, + description="The order in which to sort the news. Options are: published_at," + " updated_at, title, author, channel, ticker, topic, content_type.", + ) + isin: Optional[str] = Field( + default=None, description="The ISIN of the news to retrieve." + ) + cusip: Optional[str] = Field( + default=None, description="The CUSIP of the news to retrieve." + ) + tickers: Optional[str] = Field( + default=None, description="The tickers of the news to retrieve." + ) + channels: Optional[str] = Field( + default=None, description="The channels of the news to retrieve." + ) + topics: Optional[str] = Field( + default=None, description="The topics of the news to retrieve." + ) + authors: Optional[str] = Field( + default=None, description="The authors of the news to retrieve." + ) + content_types: Optional[str] = Field( + default=None, description="The content types of the news to retrieve." + ) -class BenzingaGlobalNewsData(BenzingaBaseNewsData): - url: str +class BenzingaGlobalNewsData(GlobalNewsData): + """Benzinga Global News data.""" + + class Config: + fields = {"date": "created", "text": "body"} + + image: List[BenzingaImage] = Field( + description="The images associated with the news." + ) + + @validator("date", pre=True) + def time_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%a, %d %b %Y %H:%M:%S %z") class BenzingaGlobalNewsFetcher( Fetcher[ - GlobalNewsQueryParams, - GlobalNewsData, BenzingaGlobalNewsQueryParams, BenzingaGlobalNewsData, ] ): @staticmethod - def transform_query( - query: GlobalNewsQueryParams, extra_params: Optional[Dict] = None - ) -> BenzingaGlobalNewsQueryParams: - return BenzingaGlobalNewsQueryParams( - page=query.page, **extra_params if extra_params else {} - ) + def transform_query(params: Dict[str, Any]) -> BenzingaGlobalNewsQueryParams: + return BenzingaGlobalNewsQueryParams(**params) @staticmethod def extract_data( - query: BenzingaGlobalNewsQueryParams, credentials: Optional[Dict[str, str]] + query: BenzingaGlobalNewsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[BenzingaGlobalNewsData]: - if credentials: - api_key = credentials.get("benzinga_api_key") + api_key = credentials.get("benzinga_api_key") if credentials else "" base_url = "https://api.benzinga.com/api/v2/news" - querystring = get_querystring(query.dict(), []) + querystring = get_querystring(query.dict(by_alias=True), []) request_url = f"{base_url}?{querystring}&token={api_key}" - data = get_data(request_url) + data = get_data(request_url, **kwargs) if len(data) == 0: raise RuntimeError("No news found") @@ -113,6 +129,5 @@ class BenzingaGlobalNewsFetcher( @staticmethod def transform_data( data: List[BenzingaGlobalNewsData], - ) -> List[GlobalNewsData]: - processors = {"image": lambda x: "" if x == [] else x[0].url} - return data_transformer(data, GlobalNewsData, processors) + ) -> List[BenzingaGlobalNewsData]: + return data diff --git a/openbb_sdk/providers/benzinga/openbb_benzinga/models/stock_news.py b/openbb_sdk/providers/benzinga/openbb_benzinga/models/stock_news.py index af6a81dfa75..d8d1c9b9d41 100644 --- a/openbb_sdk/providers/benzinga/openbb_benzinga/models/stock_news.py +++ b/openbb_sdk/providers/benzinga/openbb_benzinga/models/stock_news.py @@ -2,110 +2,121 @@ from datetime import datetime -from typing import Dict, List, Optional +from typing import Any, Dict, List, Literal, Optional -from openbb_provider.abstract.data import QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer, get_querystring -from openbb_provider.models.stock_news import StockNewsData, StockNewsQueryParams -from pydantic import Field +from openbb_provider.standard_models.stock_news import ( + StockNewsData, + StockNewsQueryParams, +) +from openbb_provider.utils.helpers import get_querystring +from pydantic import Field, validator -from openbb_benzinga.utils.helpers import BenzingaBaseNewsData, get_data +from openbb_benzinga.utils.helpers import BenzingaImage, get_data -class BenzingaStockNewsQueryParams(QueryParams): +class BenzingaStockNewsQueryParams(StockNewsQueryParams): """Benzinga Stock News query. Source: https://docs.benzinga.io/benzinga/newsfeed-v2.html - - Parameter - --------- - page : int (default: 0) - The page of the data to retrieve. - symbols: str - The symbols of the companies. - pageSize : int (default: 15) - The number of results to return per page. - headline : str - The type of data to return. Options are "headline", "summary", "full", "all". - date : Optional[datetime] - The date of the news to retrieve. - dateFrom : Optional[datetime] - The start date of the news to retrieve. - dateTo : Optional[datetime] - The end date of the news to retrieve. - updatedSince : Optional[int] - The number of seconds since the news was updated. - publishedSince : Optional[int] - The number of seconds since the news was published. - sort : Optional[str] - The order in which to sort the news. Options are: - "published_at", "updated_at", "title", "author", "channel", "ticker", "topic", "content_type". - isin : Optional[str] - The ISIN of the news to retrieve. - cusip : Optional[str] - The CUSIP of the news to retrieve. - channels : Optional[str] - The channels of the news to retrieve. - topics : Optional[str] - The topics of the news to retrieve. - authors : Optional[str] - The authors of the news to retrieve. - content_types : Optional[str] - The content types of the news to retrieve. """ - tickers: str = Field(alias="symbols") - page: int = Field(default=0) - pageSize: int = Field(default=15) - displayOutput: str = Field(default="headline") - date: Optional[datetime] = None - dateFrom: Optional[datetime] = None - dateTo: Optional[datetime] = None - updatedSince: Optional[int] = None - publishedSince: Optional[int] = None - sort: Optional[str] = None - isin: Optional[str] = None - cusip: Optional[str] = None - channels: Optional[str] = None - topics: Optional[str] = None - authors: Optional[str] = None - content_types: Optional[str] = None + class Config: + fields = {"symbols": "tickers", "limit": "pageSize"} + + displayOutput: Literal["headline", "summary", "full", "all"] = Field( + default="headline", description="The type of data to return." + ) + date: Optional[datetime] = Field( + default=None, description="The date of the news to retrieve." + ) + dateFrom: Optional[datetime] = Field( + default=None, description="The start date of the news to retrieve." + ) + dateTo: Optional[datetime] = Field( + default=None, description="The end date of the news to retrieve." + ) + updatedSince: Optional[int] = Field( + default=None, + description="The number of seconds since the news was updated.", + ) + publishedSince: Optional[int] = Field( + default=None, + description="The number of seconds since the news was published.", + ) + sort: Optional[ + Literal[ + "published_at", + "updated_at", + "title", + "author", + "channel", + "ticker", + "topic", + "content_type", + ] + ] = Field( + default=None, + description="The order in which to sort the news. Options are: published_at," + " updated_at, title, author, channel, ticker, topic, content_type.", + ) + isin: Optional[str] = Field( + default=None, description="The ISIN of the news to retrieve." + ) + cusip: Optional[str] = Field( + default=None, description="The CUSIP of the news to retrieve." + ) + channels: Optional[str] = Field( + default=None, description="The channels of the news to retrieve." + ) + topics: Optional[str] = Field( + default=None, description="The topics of the news to retrieve." + ) + authors: Optional[str] = Field( + default=None, description="The authors of the news to retrieve." + ) + content_types: Optional[str] = Field( + default=None, description="The content types of the news to retrieve." + ) -class BenzingaStockNewsData(BenzingaBaseNewsData): +class BenzingaStockNewsData(StockNewsData): """Benzinga Stock News data.""" + class Config: + fields = {"date": "created", "text": "body"} + + image: List[BenzingaImage] = Field( + description="The images associated with the news." + ) + + @validator("date", pre=True) + def time_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%a, %d %b %Y %H:%M:%S %z") + class BenzingaStockNewsFetcher( Fetcher[ - StockNewsQueryParams, - StockNewsData, BenzingaStockNewsQueryParams, BenzingaStockNewsData, ] ): @staticmethod - def transform_query( - query: StockNewsQueryParams, extra_params: Optional[Dict] = None - ) -> BenzingaStockNewsQueryParams: - return BenzingaStockNewsQueryParams( - page=query.page, - symbols=query.symbols, # type: ignore - **extra_params if extra_params else {}, - ) + def transform_query(params: Dict[str, Any]) -> BenzingaStockNewsQueryParams: + return BenzingaStockNewsQueryParams(**params) @staticmethod def extract_data( - query: BenzingaStockNewsQueryParams, credentials: Optional[Dict[str, str]] + query: BenzingaStockNewsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[BenzingaStockNewsData]: - if credentials: - api_key = credentials.get("benzinga_api_key") + api_key = credentials.get("benzinga_api_key") if credentials else "" base_url = "https://api.benzinga.com/api/v2/news" - querystring = get_querystring(query.dict(), []) + querystring = get_querystring(query.dict(by_alias=True), []) request_url = f"{base_url}?{querystring}&token={api_key}" - data = get_data(request_url) + data = get_data(request_url, **kwargs) if len(data) == 0: raise RuntimeError("No news found") @@ -113,6 +124,8 @@ class BenzingaStockNewsFetcher( return [BenzingaStockNewsData(**d) for d in data] @staticmethod - def transform_data(data: List[BenzingaStockNewsData]) -> List[StockNewsData]: - processors = {"image": lambda x: "" if x == [] else x[0].url} - return data_transformer(data, StockNewsData, processors) + def transform_data( + data: List[BenzingaStockNewsData], + ) -> List[BenzingaStockNewsData]: + # {"image": lambda x: "" if x == [] else x[0].url} + return data diff --git a/openbb_sdk/providers/benzinga/openbb_benzinga/provider.py b/openbb_sdk/providers/benzinga/openbb_benzinga/provider.py deleted file mode 100644 index 3f5eebce09b..00000000000 --- a/openbb_sdk/providers/benzinga/openbb_benzinga/provider.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Benzinga provider module.""" - - -from openbb_provider.abstract.provider import Provider - -from openbb_benzinga.models.global_news import BenzingaGlobalNewsFetcher -from openbb_benzinga.models.stock_news import BenzingaStockNewsFetcher - -# mypy: disable-error-code="list-item" - - -benzinga_provider = Provider( - name="benzinga", - description="Provider for Benzinga.", - required_credentials=["api_key"], - fetcher_list=[BenzingaGlobalNewsFetcher, BenzingaStockNewsFetcher], -) diff --git a/openbb_sdk/providers/benzinga/openbb_benzinga/utils/helpers.py b/openbb_sdk/providers/benzinga/openbb_benzinga/utils/helpers.py index 1a0ff5db7ef..aee8e7150fe 100644 --- a/openbb_sdk/providers/benzinga/openbb_benzinga/utils/helpers.py +++ b/openbb_sdk/providers/benzinga/openbb_benzinga/utils/helpers.py @@ -1,16 +1,16 @@ """Benzinga Helpers.""" -from datetime import datetime -from typing import List +from typing import Any -import requests -from openbb_provider.abstract.data import Data -from pydantic import BaseModel, Field, validator +from openbb_provider import helpers +from pydantic import BaseModel -def get_data(url: str) -> dict: - r = requests.get(url, timeout=10, headers={"accept": "application/json"}) +def get_data(url: str, **kwargs: Any) -> dict: + r = helpers.make_request( + url, timeout=10, headers={"accept": "application/json"}, **kwargs + ) if r.status_code != 200: data = r.json() if data == ['Access denied for user 0 "anonymous"']: @@ -26,15 +26,3 @@ def get_data(url: str) -> dict: class BenzingaImage(BaseModel): size: str url: str - - -class BenzingaBaseNewsData(Data): - created: datetime = Field(alias="date") - title: str - image: List[BenzingaImage] - body: str = Field(alias="text") - url: str - - @validator("created", pre=True) - def time_validate(cls, v): # pylint: disable=E0213 - return datetime.strptime(v, "%a, %d %b %Y %H:%M:%S %z") diff --git a/openbb_sdk/providers/benzinga/poetry.lock b/openbb_sdk/providers/benzinga/poetry.lock deleted file mode 100644 index 583148ff3cf..00000000000 --- a/openbb_sdk/providers/benzinga/poetry.lock +++ /dev/null @@ -1,245 +0,0 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. - -[[package]] -name = "certifi" -version = "2023.5.7" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, - {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.2.0" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, - {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, -] - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "openbb-provider" -version = "0.2.0" -description = "" -optional = false -python-versions = "^3.8" -files = [] -develop = false - -[package.dependencies] -pydantic = {version = "^1.10", extras = ["dotenv"]} -requests = "^2.31.0" -urllib3 = "<2.0.0" - -[package.source] -type = "directory" -url = "../../sdk/provider" - -[[package]] -name = "pydantic" -version = "1.10.11" -description = "Data validation and settings management using python type hints" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pydantic-1.10.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ff44c5e89315b15ff1f7fdaf9853770b810936d6b01a7bcecaa227d2f8fe444f"}, - {file = "pydantic-1.10.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a6c098d4ab5e2d5b3984d3cb2527e2d6099d3de85630c8934efcfdc348a9760e"}, - {file = "pydantic-1.10.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16928fdc9cb273c6af00d9d5045434c39afba5f42325fb990add2c241402d151"}, - {file = "pydantic-1.10.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0588788a9a85f3e5e9ebca14211a496409cb3deca5b6971ff37c556d581854e7"}, - {file = "pydantic-1.10.11-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9baf78b31da2dc3d3f346ef18e58ec5f12f5aaa17ac517e2ffd026a92a87588"}, - {file = "pydantic-1.10.11-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:373c0840f5c2b5b1ccadd9286782852b901055998136287828731868027a724f"}, - {file = "pydantic-1.10.11-cp310-cp310-win_amd64.whl", hash = "sha256:c3339a46bbe6013ef7bdd2844679bfe500347ac5742cd4019a88312aa58a9847"}, - {file = "pydantic-1.10.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:08a6c32e1c3809fbc49debb96bf833164f3438b3696abf0fbeceb417d123e6eb"}, - {file = "pydantic-1.10.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a451ccab49971af043ec4e0d207cbc8cbe53dbf148ef9f19599024076fe9c25b"}, - {file = "pydantic-1.10.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b02d24f7b2b365fed586ed73582c20f353a4c50e4be9ba2c57ab96f8091ddae"}, - {file = "pydantic-1.10.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f34739a89260dfa420aa3cbd069fbcc794b25bbe5c0a214f8fb29e363484b66"}, - {file = "pydantic-1.10.11-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e297897eb4bebde985f72a46a7552a7556a3dd11e7f76acda0c1093e3dbcf216"}, - {file = "pydantic-1.10.11-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d185819a7a059550ecb85d5134e7d40f2565f3dd94cfd870132c5f91a89cf58c"}, - {file = "pydantic-1.10.11-cp311-cp311-win_amd64.whl", hash = "sha256:4400015f15c9b464c9db2d5d951b6a780102cfa5870f2c036d37c23b56f7fc1b"}, - {file = "pydantic-1.10.11-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2417de68290434461a266271fc57274a138510dca19982336639484c73a07af6"}, - {file = "pydantic-1.10.11-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:331c031ba1554b974c98679bd0780d89670d6fd6f53f5d70b10bdc9addee1713"}, - {file = "pydantic-1.10.11-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8268a735a14c308923e8958363e3a3404f6834bb98c11f5ab43251a4e410170c"}, - {file = "pydantic-1.10.11-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:44e51ba599c3ef227e168424e220cd3e544288c57829520dc90ea9cb190c3248"}, - {file = "pydantic-1.10.11-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d7781f1d13b19700b7949c5a639c764a077cbbdd4322ed505b449d3ca8edcb36"}, - {file = "pydantic-1.10.11-cp37-cp37m-win_amd64.whl", hash = "sha256:7522a7666157aa22b812ce14c827574ddccc94f361237ca6ea8bb0d5c38f1629"}, - {file = "pydantic-1.10.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc64eab9b19cd794a380179ac0e6752335e9555d214cfcb755820333c0784cb3"}, - {file = "pydantic-1.10.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8dc77064471780262b6a68fe67e013298d130414d5aaf9b562c33987dbd2cf4f"}, - {file = "pydantic-1.10.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe429898f2c9dd209bd0632a606bddc06f8bce081bbd03d1c775a45886e2c1cb"}, - {file = "pydantic-1.10.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:192c608ad002a748e4a0bed2ddbcd98f9b56df50a7c24d9a931a8c5dd053bd3d"}, - {file = "pydantic-1.10.11-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ef55392ec4bb5721f4ded1096241e4b7151ba6d50a50a80a2526c854f42e6a2f"}, - {file = "pydantic-1.10.11-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:41e0bb6efe86281623abbeeb0be64eab740c865388ee934cd3e6a358784aca6e"}, - {file = "pydantic-1.10.11-cp38-cp38-win_amd64.whl", hash = "sha256:265a60da42f9f27e0b1014eab8acd3e53bd0bad5c5b4884e98a55f8f596b2c19"}, - {file = "pydantic-1.10.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:469adf96c8e2c2bbfa655fc7735a2a82f4c543d9fee97bd113a7fb509bf5e622"}, - {file = "pydantic-1.10.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e6cbfbd010b14c8a905a7b10f9fe090068d1744d46f9e0c021db28daeb8b6de1"}, - {file = "pydantic-1.10.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abade85268cc92dff86d6effcd917893130f0ff516f3d637f50dadc22ae93999"}, - {file = "pydantic-1.10.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9738b0f2e6c70f44ee0de53f2089d6002b10c33264abee07bdb5c7f03038303"}, - {file = "pydantic-1.10.11-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:787cf23e5a0cde753f2eabac1b2e73ae3844eb873fd1f5bdbff3048d8dbb7604"}, - {file = "pydantic-1.10.11-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:174899023337b9fc685ac8adaa7b047050616136ccd30e9070627c1aaab53a13"}, - {file = "pydantic-1.10.11-cp39-cp39-win_amd64.whl", hash = "sha256:1954f8778489a04b245a1e7b8b22a9d3ea8ef49337285693cf6959e4b757535e"}, - {file = "pydantic-1.10.11-py3-none-any.whl", hash = "sha256:008c5e266c8aada206d0627a011504e14268a62091450210eda7c07fabe6963e"}, - {file = "pydantic-1.10.11.tar.gz", hash = "sha256:f66d479cf7eb331372c470614be6511eae96f1f120344c25f3f9bb59fb1b5528"}, -] - -[package.dependencies] -python-dotenv = {version = ">=0.10.4", optional = true, markers = "extra == \"dotenv\""} -typing-extensions = ">=4.2.0" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] - -[[package]] -name = "python-dotenv" -version = "1.0.0" -description = "Read key-value pairs from a .env file and set them as environment variables" -optional = false -python-versions = ">=3.8" -files = [ - {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, - {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.7" -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, -] - -[[package]] -name = "urllib3" -version = "1.26.16" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -files = [ - {file = "urllib3-1.26.16-py2.py3-none-any.whl", hash = "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f"}, - {file = "urllib3-1.26.16.tar.gz", hash = "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.8" -content-hash = "26269b8a989dcc6ac67961b7a43d485d231c8ac878265ac90f310ced959fc532" diff --git a/openbb_sdk/providers/benzinga/pyproject.toml b/openbb_sdk/providers/benzinga/pyproject.toml index b4245da5c3a..5d2395aea5f 100644 --- a/openbb_sdk/providers/benzinga/pyproject.toml +++ b/openbb_sdk/providers/benzinga/pyproject.toml @@ -15,4 +15,4 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.poetry.plugins."openbb_provider_extension"] -benzinga = "openbb_benzinga.provider:benzinga_provider" +benzinga = "openbb_benzinga:benzinga_provider" diff --git a/openbb_sdk/providers/benzinga/tests/.gitkeep b/openbb_sdk/providers/benzinga/tests/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/providers/fmp/openbb_fmp/__init__.py b/openbb_sdk/providers/fmp/openbb_fmp/__init__.py index e69de29bb2d..e2d88e5181f 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/__init__.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/__init__.py @@ -0,0 +1,101 @@ +"""FMP Provider module.""" +from openbb_provider.abstract.provider import Provider + +from openbb_fmp.models.analyst_estimates import FMPAnalystEstimatesFetcher +from openbb_fmp.models.available_indices import FMPAvailableIndicesFetcher +from openbb_fmp.models.balance_sheet import FMPBalanceSheetFetcher +from openbb_fmp.models.balance_sheet_growth import FMPBalanceSheetGrowthFetcher +from openbb_fmp.models.cash_flow import FMPCashFlowStatementFetcher +from openbb_fmp.models.cash_flow_growth import FMPCashFlowStatementGrowthFetcher +from openbb_fmp.models.company_overview import FMPCompanyOverviewFetcher +from openbb_fmp.models.crypto_eod import FMPCryptoEODFetcher +from openbb_fmp.models.dividend_calendar import FMPDividendCalendarFetcher +from openbb_fmp.models.earnings_calendar import FMPEarningsCalendarFetcher +from openbb_fmp.models.earnings_call_transcript import FMPEarningsCallTranscriptFetcher +from openbb_fmp.models.esg_risk_rating import FMPESGRiskRatingFetcher +from openbb_fmp.models.esg_score import FMPESGScoreFetcher +from openbb_fmp.models.esg_sector import FMPESGSectorFetcher +from openbb_fmp.models.executive_compensation import FMPExecutiveCompensationFetcher +from openbb_fmp.models.forex_eod import FMPForexEODFetcher +from openbb_fmp.models.forex_pairs import FMPForexPairsFetcher +from openbb_fmp.models.global_news import FMPGlobalNewsFetcher +from openbb_fmp.models.historical_dividends import FMPHistoricalDividendsFetcher +from openbb_fmp.models.historical_employees import FMPHistoricalEmployeesFetcher +from openbb_fmp.models.historical_stock_splits import FMPHistoricalStockSplitsFetcher +from openbb_fmp.models.income_statement import FMPIncomeStatementFetcher +from openbb_fmp.models.income_statement_growth import FMPIncomeStatementGrowthFetcher +from openbb_fmp.models.institutional_ownership import FMPInstitutionalOwnershipFetcher +from openbb_fmp.models.key_executives import FMPKeyExecutivesFetcher +from openbb_fmp.models.key_metrics import FMPKeyMetricsFetcher +from openbb_fmp.models.major_indices_constituents import ( + FMPMajorIndicesConstituentsFetcher, +) +from openbb_fmp.models.major_indices_eod import FMPMajorIndicesEODFetcher +from openbb_fmp.models.price_target import FMPPriceTargetFetcher +from openbb_fmp.models.price_target_consensus import FMPPriceTargetConsensusFetcher +from openbb_fmp.models.revenue_business_line import FMPRevenueBusinessLineFetcher +from openbb_fmp.models.revenue_geographic import FMPRevenueGeographicFetcher +from openbb_fmp.models.risk_premium import FMPRiskPremiumFetcher +from openbb_fmp.models.sec_filings import FMPSECFilingsFetcher +from openbb_fmp.models.share_statistics import FMPShareStatisticsFetcher +from openbb_fmp.models.stock_eod import FMPStockEODFetcher +from openbb_fmp.models.stock_insider_trading import FMPStockInsiderTradingFetcher +from openbb_fmp.models.stock_multiples import FMPStockMultiplesFetcher +from openbb_fmp.models.stock_news import FMPStockNewsFetcher +from openbb_fmp.models.stock_ownership import FMPStockOwnershipFetcher +from openbb_fmp.models.stock_peers import FMPStockPeersFetcher +from openbb_fmp.models.stock_splits import FMPStockSplitCalendarFetcher +from openbb_fmp.models.treasury_rates import FMPTreasuryRatesFetcher + +fmp_provider = Provider( + name="fmp", + website="https://financialmodelingprep.com/", + description="""Financial Modeling Prep is a new concept that informs you about + stock market information (news, currencies, and stock prices).""", + required_credentials=["api_key"], + fetcher_dict={ + "KeyExecutives": FMPKeyExecutivesFetcher, + "StockEOD": FMPStockEODFetcher, + "GlobalNews": FMPGlobalNewsFetcher, + "StockNews": FMPStockNewsFetcher, + "IncomeStatement": FMPIncomeStatementFetcher, + "IncomeStatementGrowth": FMPIncomeStatementGrowthFetcher, + "BalanceSheet": FMPBalanceSheetFetcher, + "BalanceSheetGrowth": FMPBalanceSheetGrowthFetcher, + "CashFlowStatement": FMPCashFlowStatementFetcher, + "CashFlowStatementGrowth": FMPCashFlowStatementGrowthFetcher, + "ShareStatistics": FMPShareStatisticsFetcher, + "MajorIndicesEOD": FMPMajorIndicesEODFetcher, + "RevenueGeographic": FMPRevenueGeographicFetcher, + "RevenueBusinessLine": FMPRevenueBusinessLineFetcher, + "InstitutionalOwnership": FMPInstitutionalOwnershipFetcher, + "CompanyOverview": FMPCompanyOverviewFetcher, + "StockInsiderTrading": FMPStockInsiderTradingFetcher, + "StockOwnership": FMPStockOwnershipFetcher, + "ESGScore": FMPESGScoreFetcher, + "ESGSector": FMPESGSectorFetcher, + "ESGRiskRating": FMPESGRiskRatingFetcher, + "PriceTargetConsensus": FMPPriceTargetConsensusFetcher, + "PriceTarget": FMPPriceTargetFetcher, + "AnalystEstimates": FMPAnalystEstimatesFetcher, + "EarningsCalendar": FMPEarningsCalendarFetcher, + "EarningsCallTranscript": FMPEarningsCallTranscriptFetcher, + "HistoricalStockSplits": FMPHistoricalStockSplitsFetcher, + "StockSplitCalendar": FMPStockSplitCalendarFetcher, + "HistoricalDividends": FMPHistoricalDividendsFetcher, + "KeyMetrics": FMPKeyMetricsFetcher, + "SECFilings": FMPSECFilingsFetcher, + "TreasuryRates": FMPTreasuryRatesFetcher, + "ExecutiveCompensation": FMPExecutiveCompensationFetcher, + "CryptoEOD": FMPCryptoEODFetcher, + "ForexEOD": FMPForexEODFetcher, + "ForexPairs": FMPForexPairsFetcher, + "StockPeers": FMPStockPeersFetcher, + "StockMultiples": FMPStockMultiplesFetcher, + "HistoricalEmployees": FMPHistoricalEmployeesFetcher, + "AvailableIndices": FMPAvailableIndicesFetcher, + "RiskPremium": FMPRiskPremiumFetcher, + "MajorIndicesConstituents": FMPMajorIndicesConstituentsFetcher, + "DividendCalendar": FMPDividendCalendarFetcher, + }, +) diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/analyst_estimates.py b/openbb_sdk/providers/fmp/openbb_fmp/models/analyst_estimates.py index 5a4ba9523bc..699215f07a8 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/analyst_estimates.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/analyst_estimates.py @@ -1,13 +1,10 @@ """FMP Analyst Estimates fetcher.""" -from datetime import date as dateType -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.analyst_estimates import ( +from openbb_provider.standard_models.analyst_estimates import ( AnalystEstimatesData, AnalystEstimatesQueryParams, ) @@ -16,76 +13,67 @@ from openbb_fmp.utils.helpers import create_url, get_data_many class FMPAnalystEstimatesQueryParams(AnalystEstimatesQueryParams): - """FMP Analysts Estimates query. + """FMP Analysts Estimates Query. Source: https://site.financialmodelingprep.com/developer/docs/analyst-estimates-api/ - - Parameter - --------- - symbol : str - The symbol of the company. - period: Literal["quarter", "annual"] - The period of the analyst estimates. - limit : int - The limit number of the amount of returned estimates. """ -class FMPAnalystEstimatesData(Data): - symbol: str - date: dateType - estimatedRevenueLow: int - estimatedRevenueHigh: int - estimatedRevenueAvg: int - estimatedEbitdaLow: int - estimatedEbitdaHigh: int - estimatedEbitdaAvg: int - estimatedEbitLow: int - estimatedEbitHigh: int - estimatedEbitAvg: int - estimatedNetIncomeLow: int - estimatedNetIncomeHigh: int - estimatedNetIncomeAvg: int - estimatedSgaExpenseLow: int - estimatedSgaExpenseHigh: int - estimatedSgaExpenseAvg: int - estimatedEpsAvg: float - estimatedEpsHigh: float - estimatedEpsLow: float - numberAnalystEstimatedRevenue: int - numberAnalystsEstimatedEps: int +class FMPAnalystEstimatesData(AnalystEstimatesData): + """FMP Analyst Estimates Data.""" + + class Config: + fields = { + "estimated_revenue_low": "estimatedRevenueLow", + "estimated_revenue_high": "estimatedRevenueHigh", + "estimated_revenue_avg": "estimatedRevenueAvg", + "estimated_ebitda_low": "estimatedEbitdaLow", + "estimated_ebitda_high": "estimatedEbitdaHigh", + "estimated_ebitda_avg": "estimatedEbitdaAvg", + "estimated_ebit_low": "estimatedEbitLow", + "estimated_ebit_high": "estimatedEbitHigh", + "estimated_ebit_avg": "estimatedEbitAvg", + "estimated_net_income_low": "estimatedNetIncomeLow", + "estimated_net_income_high": "estimatedNetIncomeHigh", + "estimated_net_income_avg": "estimatedNetIncomeAvg", + "estimated_sga_expense_low": "estimatedSgaExpenseLow", + "estimated_sga_expense_high": "estimatedSgaExpenseHigh", + "estimated_sga_expense_avg": "estimatedSgaExpenseAvg", + "estimated_eps_avg": "estimatedEpsAvg", + "estimated_eps_high": "estimatedEpsHigh", + "estimated_eps_low": "estimatedEpsLow", + "number_analyst_estimated_revenue": "numberAnalystEstimatedRevenue", + "number_analysts_estimated_eps": "numberAnalystsEstimatedEps", + } class FMPAnalystEstimatesFetcher( Fetcher[ - AnalystEstimatesQueryParams, - AnalystEstimatesData, FMPAnalystEstimatesQueryParams, FMPAnalystEstimatesData, ] ): @staticmethod - def transform_query( - query: AnalystEstimatesQueryParams, extra_params: Optional[Dict] = None - ) -> FMPAnalystEstimatesQueryParams: - return FMPAnalystEstimatesQueryParams( - symbol=query.symbol, period=query.period, limit=query.limit - ) + def transform_query(params: Dict[str, Any]) -> FMPAnalystEstimatesQueryParams: + return FMPAnalystEstimatesQueryParams(**params) @staticmethod def extract_data( - query: FMPAnalystEstimatesQueryParams, credentials: Optional[Dict[str, str]] + query: FMPAnalystEstimatesQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPAnalystEstimatesData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" + + query.period = "quarter" if query.period == "quarterly" else "annual" url = create_url( 3, f"analyst-estimates/{query.symbol}", api_key, query, ["symbol"] ) - return get_data_many(url, FMPAnalystEstimatesData) + return get_data_many(url, FMPAnalystEstimatesData, **kwargs) @staticmethod def transform_data( data: List[FMPAnalystEstimatesData], - ) -> List[AnalystEstimatesData]: - return data_transformer(data, AnalystEstimatesData) + ) -> List[FMPAnalystEstimatesData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/available_indices.py b/openbb_sdk/providers/fmp/openbb_fmp/models/available_indices.py index 7f3c5101943..160ef626865 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/available_indices.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/available_indices.py @@ -1,65 +1,58 @@ """FMP Available Indices fetcher.""" -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data, QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.available_indices import ( +from openbb_provider.standard_models.available_indices import ( AvailableIndicesData, AvailableIndicesQueryParams, ) -from pydantic import Field from openbb_fmp.utils.helpers import get_data_many -class FMPAvailableIndicesQueryParams(QueryParams): - """FMP Available Indices query. +class FMPAvailableIndicesQueryParams(AvailableIndicesQueryParams): + """FMP Available Indices Query. Source: https://site.financialmodelingprep.com/developer/docs/#Historical-stock-index-prices - """ -class FMPAvailableIndicesData(Data): - """FMP Available Indices data.""" +class FMPAvailableIndicesData(AvailableIndicesData): + """FMP Available Indices Data.""" - symbol: str - name: Optional[str] - currency: Optional[str] - stockExchange: str = Field(alias="stock_exchange") - exchangeShortName: str = Field(alias="exchange_short_name") + class Config: + fields = { + "stock_exchange": "stockExchange", + "exchange_short_name": "exchangeShortName", + } class FMPAvailableIndicesFetcher( Fetcher[ - AvailableIndicesQueryParams, - AvailableIndicesData, FMPAvailableIndicesQueryParams, FMPAvailableIndicesData, ] ): @staticmethod - def transform_query( - query: AvailableIndicesQueryParams, extra_params: Optional[Dict] = None - ) -> FMPAvailableIndicesQueryParams: - return FMPAvailableIndicesQueryParams() + def transform_query(params: Dict[str, Any]) -> FMPAvailableIndicesQueryParams: + return FMPAvailableIndicesQueryParams(**params) @staticmethod def extract_data( - query: FMPAvailableIndicesQueryParams, credentials: Optional[Dict[str, str]] + query: FMPAvailableIndicesQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPAvailableIndicesData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" base_url = "https://financialmodelingprep.com/api/v3" url = f"{base_url}/symbol/available-indexes?apikey={api_key}" - return get_data_many(url, FMPAvailableIndicesData) + return get_data_many(url, FMPAvailableIndicesData, **kwargs) @staticmethod def transform_data( data: List[FMPAvailableIndicesData], - ) -> List[AvailableIndicesData]: - return data_transformer(data, AvailableIndicesData) + ) -> List[FMPAvailableIndicesData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/balance_sheet.py b/openbb_sdk/providers/fmp/openbb_fmp/models/balance_sheet.py index d54a7c9d217..bea6ba4e30e 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/balance_sheet.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/balance_sheet.py @@ -1,45 +1,25 @@ """FMP Balance Sheet Fetcher.""" -from datetime import ( - date as dateType, - datetime, -) -from typing import Dict, List, Literal, Optional +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data, QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.balance_sheet import ( +from openbb_provider.standard_models.balance_sheet import ( BalanceSheetData, BalanceSheetQueryParams, ) -from pydantic import Field, NonNegativeInt, root_validator +from pydantic import root_validator from openbb_fmp.utils.helpers import create_url, get_data_many -class FMPBalanceSheetQueryParams(QueryParams): +class FMPBalanceSheetQueryParams(BalanceSheetQueryParams): """FMP Balance Sheet QueryParams. Source: https://financialmodelingprep.com/developer/docs/#Balance-Sheet - - Parameter - --------- - symbol : Optional[str] - The symbol of the company if cik is not provided. - cik : Optional[str] - The CIK of the company if symbol is not provided. - period : Literal["annual", "quarter"] - The period of the balance sheet. Default is "annual". - limit : Optional[NonNegativeInt] - The limit of the balance sheet. """ - symbol: Optional[str] cik: Optional[str] - period: Literal["annual", "quarter"] = Field(default="annual") - limit: Optional[NonNegativeInt] @root_validator() def check_symbol_or_cik(cls, values): # pylint: disable=no-self-argument @@ -48,97 +28,92 @@ class FMPBalanceSheetQueryParams(QueryParams): return values -class FMPBalanceSheetData(Data): - date: dateType - symbol: str - cik: Optional[int] - reportedCurrency: Optional[str] - fillingDate: Optional[dateType] - acceptedDate: Optional[datetime] - calendarYear: Optional[int] - period: Optional[str] +class FMPBalanceSheetData(BalanceSheetData): + """FMP Balance Sheet Data.""" - cashAndCashEquivalents: Optional[int] - shortTermInvestments: Optional[int] - cashAndShortTermInvestments: Optional[int] - netReceivables: Optional[int] - inventory: Optional[int] - otherCurrentAssets: Optional[int] - totalCurrentAssets: Optional[int] = Field(alias="current_assets") - - longTermInvestments: Optional[int] - propertyPlantEquipmentNet: Optional[int] - - goodwill: Optional[int] - intangibleAssets: Optional[int] - goodwillAndIntangibleAssets: Optional[int] - taxAssets: Optional[int] - otherNonCurrentAssets: Optional[int] - - totalNonCurrentAssets: Optional[int] = Field(alias="noncurrent_assets") - totalAssets: Optional[int] = Field(alias="assets") - - accountPayables: Optional[int] - otherCurrentLiabilities: Optional[int] - deferredRevenue: Optional[int] - shortTermDebt: Optional[int] - taxPayables: Optional[int] - totalCurrentLiabilities: Optional[int] = Field(alias="current_liabilities") - - longTermDebt: Optional[int] - deferredRevenueNonCurrent: Optional[int] - deferredTaxLiabilitiesNonCurrent: Optional[int] - otherNonCurrentLiabilities: Optional[int] - totalNonCurrentLiabilities: Optional[int] = Field(alias="noncurrent_liabilities") - totalLiabilities: Optional[int] = Field(alias="liabilities") - - commonStock: Optional[int] - retainedEarnings: Optional[int] - accumulatedOtherComprehensiveIncomeLoss: Optional[int] - othertotalStockholdersEquity: Optional[int] - totalEquity: Optional[int] - totalLiabilitiesAndStockholdersEquity: Optional[int] + class Config: + fields = { + "currency": "reportedCurrency", + "filing_date": "fillingDate", + "accepted_date": "acceptedDate", + "cash_and_cash_equivalents": "cashAndCashEquivalents", + "short_term_investments": "shortTermInvestments", + "net_receivables": "netReceivables", + "other_current_assets": "otherCurrentAssets", + "current_assets": "totalCurrentAssets", + "long_term_investments": "longTermInvestments", + "property_plant_equipment_net": "propertyPlantEquipmentNet", + "intangible_assets": "intangibleAssets", + "other_non_current_assets": "otherNonCurrentAssets", + "tax_assets": "taxAssets", + "other_assets": "otherAssets", + "noncurrent_assets": "totalNonCurrentAssets", + "assets": "totalAssets", + "account_payables": "accountPayables", + "other_current_liabilities": "otherCurrentLiabilities", + "tax_payables": "taxPayables", + "short_term_debt": "shortTermDebt", + "deferred_revenue": "deferredRevenue", + "current_liabilities": "totalCurrentLiabilities", + "long_term_debt": "longTermDebt", + "other_non_current_liabilities": "otherNonCurrentLiabilities", + "other_liabilities": "otherLiabilities", + "noncurrent_liabilities": "totalNonCurrentLiabilities", + "liabilities": "totalLiabilities", + "common_stock": "commonStock", + "other_stockholder_equity": "othertotalStockholdersEquity", + "accumulated_other_comprehensive_income_loss": "accumulatedOtherComprehensiveIncomeLoss", + "preferred_stock": "preferredStock", + "retained_earnings": "retainedEarnings", + "minority_interest": "minorityInterest", + "total_stockholders_equity": "totalStockholdersEquity", + "total_equity": "totalEquity", + "total_liabilities_and_stockholders_equity": "totalLiabilitiesAndStockholdersEquity", + "total_liabilities_and_total_equity": "totalLiabilitiesAndTotalEquity", + } # Leftovers below - totalStockholdersEquity: Optional[int] - minorityInterest: Optional[int] - totalLiabilitiesAndTotalEquity: Optional[int] - totalInvestments: Optional[int] - netDebt: Optional[int] + calendarYear: Optional[int] + link: Optional[str] finalLink: Optional[str] + cashAndShortTermInvestments: Optional[int] + goodwillAndIntangibleAssets: Optional[int] + deferredRevenueNonCurrent: Optional[int] + totalInvestments: Optional[int] + + capitalLeaseObligations: Optional[int] + deferredTaxLiabilitiesNonCurrent: Optional[int] + totalNonCurrentLiabilities: Optional[int] + totalDebt: Optional[int] + netDebt: Optional[int] + class FMPBalanceSheetFetcher( Fetcher[ - BalanceSheetQueryParams, - BalanceSheetData, FMPBalanceSheetQueryParams, FMPBalanceSheetData, ] ): @staticmethod - def transform_query( - query: BalanceSheetQueryParams, extra_params: Optional[Dict] = None - ) -> FMPBalanceSheetQueryParams: - period = "annual" if query.period == "annually" else "quarter" - return FMPBalanceSheetQueryParams( - symbol=query.symbol, period=period, **extra_params if extra_params else {} # type: ignore - ) + def transform_query(params: Dict[str, Any]) -> FMPBalanceSheetQueryParams: + return FMPBalanceSheetQueryParams(**params) @staticmethod def extract_data( - query: FMPBalanceSheetQueryParams, credentials: Optional[Dict[str, str]] + query: FMPBalanceSheetQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPBalanceSheetData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url( 3, f"balance-sheet-statement/{query.symbol}", api_key, query, ["symbol"] ) - return get_data_many(url, FMPBalanceSheetData) + return get_data_many(url, FMPBalanceSheetData, **kwargs) @staticmethod def transform_data( data: List[FMPBalanceSheetData], - ) -> List[BalanceSheetData]: - return data_transformer(data, BalanceSheetData) + ) -> List[FMPBalanceSheetData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/balance_sheet_growth.py b/openbb_sdk/providers/fmp/openbb_fmp/models/balance_sheet_growth.py new file mode 100644 index 00000000000..43dc307da35 --- /dev/null +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/balance_sheet_growth.py @@ -0,0 +1,106 @@ +"""FMP Balance Sheet Growth Fetcher.""" + + +from datetime import datetime +from typing import Any, Dict, List, Optional + +from openbb_provider.abstract.fetcher import Fetcher +from openbb_provider.standard_models.balance_sheet_growth import ( + BalanceSheetGrowthData, + BalanceSheetGrowthQueryParams, +) +from pydantic import validator + +from openbb_fmp.utils.helpers import create_url, get_data_many + + +class FMPBalanceSheetGrowthQueryParams(BalanceSheetGrowthQueryParams): + """FMP Balance Sheet Growth QueryParams. + + Source: https://site.financialmodelingprep.com/developer/docs/#Financial-Statements-Growth + """ + + +class FMPBalanceSheetGrowthData(BalanceSheetGrowthData): + """FMP Balance Sheet Growth Data.""" + + class Config: + fields = { + "growth_cash_and_cash_equivalents": "growthCashAndCashEquivalents", + "growth_short_term_investments": "growthShortTermInvestments", + "growth_cash_and_short_term_investments": "growthCashAndShortTermInvestments", + "growth_net_receivables": "growthNetReceivables", + "growth_inventory": "growthInventory", + "growth_other_current_assets": "growthOtherCurrentAssets", + "growth_total_current_assets": "growthTotalCurrentAssets", + "growth_property_plant_equipment_net": "growthPropertyPlantEquipmentNet", + "growth_goodwill": "growthGoodwill", + "growth_intangible_assets": "growthIntangibleAssets", + "growth_goodwill_and_intangible_assets": "growthGoodwillAndIntangibleAssets", + "growth_long_term_investments": "growthLongTermInvestments", + "growth_tax_assets": "growthTaxAssets", + "growth_other_non_current_assets": "growthOtherNonCurrentAssets", + "growth_total_non_current_assets": "growthTotalNonCurrentAssets", + "growth_other_assets": "growthOtherAssets", + "growth_total_assets": "growthTotalAssets", + "growth_account_payables": "growthAccountPayables", + "growth_short_term_debt": "growthShortTermDebt", + "growth_tax_payables": "growthTaxPayables", + "growth_deferred_revenue": "growthDeferredRevenue", + "growth_other_current_liabilities": "growthOtherCurrentLiabilities", + "growth_total_current_liabilities": "growthTotalCurrentLiabilities", + "growth_long_term_debt": "growthLongTermDebt", + "growth_deferred_revenue_non_current": "growthDeferredRevenueNonCurrent", + "growth_deferrred_tax_liabilities_non_current": "growthDeferrredTaxLiabilitiesNonCurrent", + "growth_other_non_current_liabilities": "growthOtherNonCurrentLiabilities", + "growth_total_non_current_liabilities": "growthTotalNonCurrentLiabilities", + "growth_other_liabilities": "growthOtherLiabilities", + "growth_total_liabilities": "growthTotalLiabilities", + "growth_common_stock": "growthCommonStock", + "growth_retained_earnings": "growthRetainedEarnings", + "growth_accumulated_other_comprehensive_income_loss": "growthAccumulatedOtherComprehensiveIncomeLoss", + "growth_othertotal_stockholders_equity": "growthOthertotalStockholdersEquity", + "growth_total_stockholders_equity": "growthTotalStockholdersEquity", + "growth_total_liabilities_and_stockholders_equity": "growthTotalLiabilitiesAndStockholdersEquity", + "growth_total_investments": "growthTotalInvestments", + "growth_total_debt": "growthTotalDebt", + "growth_net_debt": "growthNetDebt", + } + + @validator("date", pre=True, check_fields=False) + def date_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d") + + +class FMPBalanceSheetGrowthFetcher( + Fetcher[ + FMPBalanceSheetGrowthQueryParams, + FMPBalanceSheetGrowthData, + ] +): + @staticmethod + def transform_query(params: Dict[str, Any]) -> FMPBalanceSheetGrowthQueryParams: + return FMPBalanceSheetGrowthQueryParams(**params) + + @staticmethod + def extract_data( + query: FMPBalanceSheetGrowthQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> List[FMPBalanceSheetGrowthData]: + api_key = credentials.get("fmp_api_key") if credentials else "" + + url = create_url( + 3, + f"balance-sheet-statement-growth/{query.symbol}", + api_key, + query, + ["symbol"], + ) + return get_data_many(url, FMPBalanceSheetGrowthData, **kwargs) + + @staticmethod + def transform_data( + data: List[FMPBalanceSheetGrowthData], + ) -> List[FMPBalanceSheetGrowthData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/cash_flow.py b/openbb_sdk/providers/fmp/openbb_fmp/models/cash_flow.py index 6e0dda9f7bb..d8652585cbc 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/cash_flow.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/cash_flow.py @@ -1,133 +1,106 @@ """FMP Cash Flow Statement Fetcher.""" -from datetime import ( - date as dateType, - datetime, -) -from typing import Dict, List, Literal, Optional +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data, QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.cash_flows import ( +from openbb_provider.standard_models.cash_flows import ( CashFlowStatementData, CashFlowStatementQueryParams, ) -from pydantic import Field, NonNegativeInt, root_validator +from pydantic import Field, root_validator from openbb_fmp.utils.helpers import create_url, get_data_many -class FMPCashFlowStatementQueryParams(QueryParams): - """FMP Cash Flow Statement QueryParams. +class FMPCashFlowStatementQueryParams(CashFlowStatementQueryParams): + """FMP Cash Flow Statement Query. Source: https://financialmodelingprep.com/developer/docs/#Cash-Flow-Statement - - Parameter - --------- - symbol : Optional[str] - The symbol of the company if cik is not provided. - cik : Optional[str] - The CIK of the company if symbol is not provided. - period : Literal["annual", "quarter"] - The period of the cash flow statement. Default is "annual". - limit : Optional[NonNegativeInt] - The limit of the cash flow statement. """ - symbol: Optional[str] - cik: Optional[str] - period: Literal["annual", "quarter"] = Field(default="annual") - limit: Optional[NonNegativeInt] + cik: Optional[str] = Field(description="Central Index Key (CIK) of the company.") @root_validator() - def check_symbol_or_cik(cls, values): # pylint: disable=E0213 + def check_symbol_or_cik(cls, values): # pylint: disable=no-self-argument if values.get("symbol") is None and values.get("cik") is None: raise ValueError("symbol or cik must be provided") return values -class FMPCashFlowStatementData(Data): - date: dateType - symbol: str - cik: Optional[int] - reportedCurrency: str = Field(alias="currency") - fillingDate: Optional[dateType] - acceptedDate: Optional[datetime] +class FMPCashFlowStatementData(CashFlowStatementData): + """FMP Cash Flow Statement Data.""" + + class Config: + fields = { + "currency": "reportedCurrency", + "filing_date": "fillingDate", + "accepted_date": "acceptedDate", + "net_income": "netIncome", + "depreciation_and_amortization": "depreciationAndAmortization", + "stock_based_compensation": "stockBasedCompensation", + "other_non_cash_items": "otherNonCashItems", + "deferred_income_tax": "deferredIncomeTax", + "change_in_working_capital": "changeInWorkingCapital", + "accounts_receivables": "accountsReceivables", + "inventory": "inventory", + "accounts_payables": "accountsPayables", + "other_working_capital": "otherWorkingCapital", + "net_cash_flow_from_operating_activities": "netCashProvidedByOperatingActivities", + "investments_in_property_plant_and_equipment": "investmentsInPropertyPlantAndEquipment", + "acquisitions_net": "acquisitionsNet", + "purchases_of_investments": "purchasesOfInvestments", + "sales_maturities_of_investments": "salesMaturitiesOfInvestments", + "other_investing_activities": "otherInvestingActivites", + "net_cash_flow_from_investing_activities": "netCashUsedForInvestingActivites", + "debt_repayment": "debtRepayment", + "common_stock_issued": "commonStockIssued", + "common_stock_repurchased": "commonStockRepurchased", + "dividends_paid": "dividendsPaid", + "other_financing_activities": "otherFinancingActivites", + "net_cash_flow_from_financing_activities": "netCashUsedProvidedByFinancingActivities", + "effect_of_forex_changes_on_cash": "effectOfForexChangesOnCash", + "net_change_in_cash": "netChangeInCash", + "cash_at_end_of_period": "cashAtEndOfPeriod", + "cash_at_beginning_of_period": "cashAtBeginningOfPeriod", + "operating_cash_flow": "operatingCashFlow", + "capital_expenditure": "capitalExpenditure", + "net_cash_flow": "freeCashFlow", + } + + # Leftovers below calendarYear: Optional[int] - period: Optional[str] - netIncome: Optional[int] - depreciationAndAmortization: Optional[int] - deferredIncomeTax: Optional[int] - stockBasedCompensation: Optional[int] - changeInWorkingCapital: Optional[int] - accountsReceivables: Optional[int] - inventory: Optional[int] - accountsPayables: Optional[int] - otherWorkingCapital: Optional[int] - otherNonCashItems: Optional[int] - netCashProvidedByOperatingActivities: Optional[int] = Field( - alias="net_cash_flow_from_operating_activities" - ) - investmentsInPropertyPlantAndEquipment: Optional[int] - acquisitionsNet: Optional[int] - purchasesOfInvestments: Optional[int] - salesMaturitiesOfInvestments: Optional[int] - otherInvestingActivites: Optional[int] = Field(alias="other_investing_activities") - netCashUsedForInvestingActivites: Optional[int] = Field( - alias="net_cash_flow_from_investing_activities" - ) - debtRepayment: Optional[int] - commonStockIssued: Optional[int] - commonStockRepurchased: Optional[int] - dividendsPaid: Optional[int] - otherFinancingActivites: Optional[int] = Field(alias="other_financing_activities") - netCashUsedProvidedByFinancingActivities: Optional[int] = Field( - alias="net_cash_flow_from_financing_activities" - ) - effectOfForexChangesOnCash: Optional[int] = Field(alias="exchange_gain_losses") - netChangeInCash: Optional[int] = Field(alias="net_cash_flow") - cashAtEndOfPeriod: Optional[int] - cashAtBeginningOfPeriod: Optional[int] - operatingCashFlow: Optional[int] - capitalExpenditure: Optional[int] - freeCashFlow: Optional[int] link: Optional[str] finalLink: Optional[str] class FMPCashFlowStatementFetcher( Fetcher[ - CashFlowStatementQueryParams, - CashFlowStatementData, FMPCashFlowStatementQueryParams, FMPCashFlowStatementData, ] ): @staticmethod - def transform_query( - query: CashFlowStatementQueryParams, extra_params: Optional[Dict] = None - ) -> FMPCashFlowStatementQueryParams: - period = "annual" if query.period == "annually" else "quarter" - return FMPCashFlowStatementQueryParams( - symbol=query.symbol, period=period, **extra_params if extra_params else {} # type: ignore - ) + def transform_query(params: Dict[str, Any]) -> FMPCashFlowStatementQueryParams: + return FMPCashFlowStatementQueryParams(**params) @staticmethod def extract_data( - query: FMPCashFlowStatementQueryParams, credentials: Optional[Dict[str, str]] + query: FMPCashFlowStatementQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPCashFlowStatementData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" + + query.period = "annual" if query.period == "annually" else "quarter" url = create_url( 3, f"cash-flow-statement/{query.symbol}", api_key, query, ["symbol"] ) - return get_data_many(url, FMPCashFlowStatementData) + return get_data_many(url, FMPCashFlowStatementData, **kwargs) @staticmethod def transform_data( data: List[FMPCashFlowStatementData], - ) -> List[CashFlowStatementData]: - return data_transformer(data, CashFlowStatementData) + ) -> List[FMPCashFlowStatementData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/cash_flow_growth.py b/openbb_sdk/providers/fmp/openbb_fmp/models/cash_flow_growth.py new file mode 100644 index 00000000000..bd4e0754400 --- /dev/null +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/cash_flow_growth.py @@ -0,0 +1,95 @@ +"""FMP Cash Flow Statement Growth Fetcher.""" + + +from datetime import datetime +from typing import Any, Dict, List, Optional + +from openbb_provider.abstract.fetcher import Fetcher +from openbb_provider.standard_models.cash_flow_growth import ( + CashFlowStatementGrowthData, + CashFlowStatementGrowthQueryParams, +) +from pydantic import validator + +from openbb_fmp.utils.helpers import create_url, get_data_many + + +class FMPCashFlowStatementGrowthQueryParams(CashFlowStatementGrowthQueryParams): + """FMP Cash Flow Statement Growth QueryParams. + + Source: https://site.financialmodelingprep.com/developer/docs/financial-statements-growth-api/ + """ + + +class FMPCashFlowStatementGrowthData(CashFlowStatementGrowthData): + """FMP Cash Flow Statement Growth Data.""" + + class Config: + fields = { + "growth_net_income": "growthNetIncome", + "growth_depreciation_and_amortization": "growthDepreciationAndAmortization", + "growth_deferred_income_tax": "growthDeferredIncomeTax", + "growth_stock_based_compensation": "growthStockBasedCompensation", + "growth_change_in_working_capital": "growthChangeInWorkingCapital", + "growth_accounts_receivables": "growthAccountsReceivables", + "growth_inventory": "growthInventory", + "growth_accounts_payables": "growthAccountsPayables", + "growth_other_working_capital": "growthOtherWorkingCapital", + "growth_other_non_cash_items": "growthOtherNonCashItems", + "growth_net_cash_provided_by_operating_activities": "growthNetCashProvidedByOperatingActivites", + "growth_investments_in_property_plant_and_equipment": "growthInvestmentsInPropertyPlantAndEquipment", + "growth_acquisitions_net": "growthAcquisitionsNet", + "growth_purchases_of_investments": "growthPurchasesOfInvestments", + "growth_sales_maturities_of_investments": "growthSalesMaturitiesOfInvestments", + "growth_other_investing_activities": "growthOtherInvestingActivites", + "growth_net_cash_used_for_investing_activities": "growthNetCashUsedForInvestingActivites", + "growth_debt_repayment": "growthDebtRepayment", + "growth_common_stock_issued": "growthCommonStockIssued", + "growth_common_stock_repurchased": "growthCommonStockRepurchased", + "growth_dividends_paid": "growthDividendsPaid", + "growth_other_financing_activities": "growthOtherFinancingActivites", + "growth_net_cash_used_provided_by_financing_activities": "growthNetCashUsedProvidedByFinancingActivities", + "growth_effect_of_forex_changes_on_cash": "growthEffectOfForexChangesOnCash", + "growth_net_change_in_cash": "growthNetChangeInCash", + "growth_cash_at_end_of_period": "growthCashAtEndOfPeriod", + "growth_cash_at_beginning_of_period": "growthCashAtBeginningOfPeriod", + "growth_operating_cash_flow": "growthOperatingCashFlow", + "growth_capital_expenditure": "growthCapitalExpenditure", + "growth_free_cash_flow": "growthFreeCashFlow", + } + + @validator("date", pre=True, check_fields=False) + def date_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d") + + +class FMPCashFlowStatementGrowthFetcher( + Fetcher[ + FMPCashFlowStatementGrowthQueryParams, + FMPCashFlowStatementGrowthData, + ] +): + @staticmethod + def transform_query( + params: Dict[str, Any] + ) -> FMPCashFlowStatementGrowthQueryParams: + return FMPCashFlowStatementGrowthQueryParams(**params) + + @staticmethod + def extract_data( + query: FMPCashFlowStatementGrowthQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> List[FMPCashFlowStatementGrowthData]: + api_key = credentials.get("fmp_api_key") if credentials else "" + + url = create_url( + 3, f"cash-flow-statement-growth/{query.symbol}", api_key, query, ["symbol"] + ) + return get_data_many(url, FMPCashFlowStatementGrowthData, **kwargs) + + @staticmethod + def transform_data( + data: List[FMPCashFlowStatementGrowthData], + ) -> List[FMPCashFlowStatementGrowthData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/company_overview.py b/openbb_sdk/providers/fmp/openbb_fmp/models/company_overview.py new file mode 100644 index 00000000000..ec28fdcb537 --- /dev/null +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/company_overview.py @@ -0,0 +1,76 @@ +"""FMP Company Overview Fetcher.""" + + +from typing import Any, Dict, List, Optional + +from openbb_provider.abstract.fetcher import Fetcher +from openbb_provider.standard_models.company_overview import ( + CompanyOverviewData, + CompanyOverviewQueryParams, +) + +from openbb_fmp.utils.helpers import get_data + +# This part is only provided by FMP and not by the other providers for now. + + +class FMPCompanyOverviewQueryParams(CompanyOverviewQueryParams): + """FMP Company Overview Query. + + Source: https://site.financialmodelingprep.com/developer/docs/companies-key-stats-free-api/ + """ + + +class FMPCompanyOverviewData(CompanyOverviewData): + """FMP Company Overview Data.""" + + class Config: + fields = { + "vol_avg": "volAvg", + "mkt_cap": "mktCap", + "last_div": "lastDiv", + "company_name": "companyName", + "exchange_short_name": "exchangeShortName", + "full_time_employees": "fullTimeEmployees", + "dcf_diff": "dcfDiff", + "ipo_date": "ipoDate", + "default_image": "defaultImage", + "is_etf": "isEtf", + "is_actively_trading": "isActivelyTrading", + "is_adr": "isAdr", + "is_fund": "isFund", + } + + +class FMPCompanyOverviewFetcher( + Fetcher[ + FMPCompanyOverviewQueryParams, + FMPCompanyOverviewData, + ] +): + @staticmethod + def transform_query(params: Dict[str, Any]) -> FMPCompanyOverviewQueryParams: + return FMPCompanyOverviewQueryParams(**params) + + @staticmethod + def extract_data( + query: FMPCompanyOverviewQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> List[FMPCompanyOverviewData]: + api_key = credentials.get("fmp_api_key") if credentials else "" + + base_url = "https://financialmodelingprep.com/api/v3/" + request_url = f"{base_url}profile/{query.symbol}?apikey={api_key}" + data = get_data(request_url, **kwargs) + if isinstance(data, dict): + raise ValueError("Expected list of dicts, got dict") + + return [FMPCompanyOverviewData.parse_obj(data[0])] + + @staticmethod + def transform_data( # type: ignore + data: List[FMPCompanyOverviewData], + ) -> List[FMPCompanyOverviewData]: + # Need to update transform_data to return a non list version of list isn't used + return data # type: ignore diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/company_profile.py b/openbb_sdk/providers/fmp/openbb_fmp/models/company_profile.py deleted file mode 100644 index c5f385dfcc1..00000000000 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/company_profile.py +++ /dev/null @@ -1,104 +0,0 @@ -"""FMP Company Overview Fetcher.""" - - -from typing import Dict, Optional - -from openbb_provider.abstract.data import Data -from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.company_overview import ( - CompanyOverviewData, - CompanyOverviewQueryParams, -) - -from openbb_fmp.utils.helpers import get_data - -# This part is only provided by FMP and not by the other providers for now. - - -class FMPCompanyOverviewQueryParams(CompanyOverviewQueryParams): - """FMP Company Overview query. - - Source: https://site.financialmodelingprep.com/developer/docs/companies-key-stats-free-api/ - - Parameter - --------- - symbol : str - The symbol of the company. - """ - - -class FMPCompanyOverviewData(Data): - symbol: str - price: float - beta: float - volAvg: int - mktCap: int - lastDiv: float - range: str - changes: float - companyName: str - currency: str - cik: Optional[str] - isin: Optional[str] - cusip: Optional[str] - exchange: str - exchangeShortName: str - industry: str - website: str - description: str - ceo: str - sector: str - country: str - fullTimeEmployees: str - phone: str - address: str - city: str - state: str - zip: str - dcfDiff: float - dcf: float - image: str - ipoDate: str - defaultImage: bool - isEtf: bool - isActivelyTrading: bool - isAdr: bool - isFund: bool - - -class FMPCompanyProfileFetcher( - Fetcher[ # type: ignore - CompanyOverviewQueryParams, - CompanyOverviewData, - FMPCompanyOverviewQueryParams, - FMPCompanyOverviewData, - ] -): - @staticmethod - def transform_query( - query: CompanyOverviewQueryParams, extra_params: Optional[Dict] = None - ) -> FMPCompanyOverviewQueryParams: - return FMPCompanyOverviewQueryParams(symbol=query.symbol) - - @staticmethod - def extract_data( - query: FMPCompanyOverviewQueryParams, credentials: Optional[Dict[str, str]] - ) -> FMPCompanyOverviewData: - if credentials: - api_key = credentials.get("fmp_api_key") - - base_url = "https://financialmodelingprep.com/api/v3/" - request_url = f"{base_url}profile/{query.symbol}?apikey={api_key}" - data = get_data(request_url) - if isinstance(data, dict): - raise ValueError("Expected list of dicts, got dict") - - return FMPCompanyOverviewData.parse_obj(data[0]) - - @staticmethod - def transform_data( # type: ignore - data: FMPCompanyOverviewData, - ) -> CompanyOverviewData: - # Need to update transform_data to return a non list version of list isn't used - return data_transformer(data, CompanyOverviewData) # type: ignore diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/crypto_eod.py b/openbb_sdk/providers/fmp/openbb_fmp/models/crypto_eod.py index e9d4d017efc..17429fd08b1 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/crypto_eod.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/crypto_eod.py @@ -1,99 +1,90 @@ """FMP Cryptos end of day fetcher.""" -from datetime import date, datetime -from typing import Dict, List, Literal, Optional +from datetime import datetime, timedelta +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data, QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer, get_querystring -from openbb_provider.models.crypto_eod import CryptoEODData, CryptoEODQueryParams +from openbb_provider.standard_models.crypto_eod import ( + CryptoEODData, + CryptoEODQueryParams, +) +from openbb_provider.utils.helpers import get_querystring from pydantic import Field, NonNegativeInt, validator from openbb_fmp.utils.helpers import get_data_many -class FMPCryptoEODQueryParams(QueryParams): - """FMP Crypto end of day query. +class FMPCryptoEODQueryParams(CryptoEODQueryParams): + """FMP Crypto end of day Query. - Source: https://site.financialmodelingprep.com/developer/docs/#Cryptocurrencies - - Parameter - --------- - symbol : str - The symbol of the company. - start_date : date - The start date of the stock data from which to retrieve the data. - end_date : date - The end date of the stock data up to which to retrieve the data. - timeseries : Optional[int] - The number of days to look back. - serietype : Optional[Literal["line"]] - The type of the series. Only "line" is supported. + Source: https://site.financialmodelingprep.com/developer/docs/cryptocurrency-historical-data-api/#Historical-Daily-Prices """ - symbol: str = Field(min_length=1) - serietype: Optional[Literal["line"]] - start_date: date - end_date: date - timeseries: Optional[NonNegativeInt] # Number of days to looks back + timeseries: Optional[NonNegativeInt] = Field( + default=None, description="Number of days to look back." + ) -class FMPCryptoEODData(Data): - date: datetime - open: float - high: float - low: float - close: float - adjClose: float = Field(alias="adj_close") - volume: float - unadjustedVolume: float - change: float - changePercent: float - vwap: float - label: str - changeOverTime: float +class FMPCryptoEODData(CryptoEODData): + """FMP Crypto end of day Data.""" + + adjClose: float = Field( + description="Adjusted Close Price of the symbol.", alias="adj_close" + ) + unadjustedVolume: float = Field( + description="Unadjusted volume of the symbol.", alias="unadjusted_volume" + ) + change: float = Field( + description="Change in the price of the symbol from the previous day.", + alias="change", + ) + changePercent: float = Field( + description=r"Change \% in the price of the symbol.", alias="change_percent" + ) + label: str = Field(description="Human readable format of the date.") + changeOverTime: float = Field( + description=r"Change \% in the price of the symbol over a period of time.", + alias="change_over_time", + ) @validator("date", pre=True) - def time_validate(cls, v): # pylint: disable=E0213 + def date_validate(cls, v): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") class FMPCryptoEODFetcher( Fetcher[ - CryptoEODQueryParams, - CryptoEODData, FMPCryptoEODQueryParams, FMPCryptoEODData, ] ): @staticmethod - def transform_query( - query: CryptoEODQueryParams, extra_params: Optional[Dict] = None - ) -> FMPCryptoEODQueryParams: - now = datetime.now() - start_date = query.start_date if query.start_date else now - end_date = query.end_date if query.end_date else now - return FMPCryptoEODQueryParams( - symbol=query.symbol, - start_date=start_date, - end_date=end_date, - **extra_params if extra_params else {}, - ) + def transform_query(params: Dict[str, Any]) -> FMPCryptoEODQueryParams: + now = datetime.now().date() + transformed_params = params + if params.get("start_date") is None: + transformed_params["start_date"] = now - timedelta(days=7) + + if params.get("end_date") is None: + transformed_params["end_date"] = now + return FMPCryptoEODQueryParams(**transformed_params) @staticmethod def extract_data( - query: FMPCryptoEODQueryParams, credentials: Optional[Dict[str, str]] + query: FMPCryptoEODQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPCryptoEODData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" base_url = "https://financialmodelingprep.com/api/v3/" - query_str = get_querystring(query.dict(), ["symbol"]) + query_str = get_querystring(query.dict(by_alias=True), ["symbol"]) query_str = query_str.replace("start_date", "from").replace("end_date", "to") - url = f"{base_url}historical-price-full/{query.symbol}?{query_str}&apikey={api_key}" - return get_data_many(url, FMPCryptoEODData, "historical") + url = f"{base_url}historical-price-full/crypto/{query.symbol}?{query_str}&apikey={api_key}" + + return get_data_many(url, FMPCryptoEODData, "historical", **kwargs) @staticmethod - def transform_data(data: List[FMPCryptoEODData]) -> List[CryptoEODData]: - return data_transformer(data, CryptoEODData) + def transform_data(data: List[FMPCryptoEODData]) -> List[FMPCryptoEODData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/crypto_price.py b/openbb_sdk/providers/fmp/openbb_fmp/models/crypto_price.py deleted file mode 100644 index 597de3a3567..00000000000 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/crypto_price.py +++ /dev/null @@ -1,76 +0,0 @@ -"""FMP Crypto Price fetcher.""" - - -from enum import Enum -from typing import Dict, List, Optional - -from openbb_provider.abstract.data import QueryParams -from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.crypto_price import CryptoPriceData, CryptoPriceQueryParams -from pydantic import Field - -from openbb_fmp.utils.helpers import BaseStockPriceData, create_url, get_data_many - - -class Interval(str, Enum): - oneminute = "1min" - fiveminute = "5min" - fifteenminute = "15min" - thirtyminute = "30min" - onehour = "1hour" - fourhour = "4hour" - - -class FMPCryptoPriceQueryParams(QueryParams): - """FMP Crypto Price query. - - Source: https://site.financialmodelingprep.com/developer/docs/#Cryptocurrencies - - Parameter - --------- - symbol : str - The symbol of the company. - interval : Enum of ["1min", "5min", "15min", "30min", "1hour", "4hour"] (default: "1hour") - The interval of the data. - """ - - symbol: str = Field(min_length=1) - interval: Interval = Field(default=Interval.onehour) - - -class FMPCryptoPriceData(BaseStockPriceData): - """FMP Crypto Price data.""" - - -class FMPCryptoPriceFetcher( - Fetcher[ - CryptoPriceQueryParams, - CryptoPriceData, - FMPCryptoPriceQueryParams, - FMPCryptoPriceData, - ] -): - @staticmethod - def transform_query( - query: CryptoPriceQueryParams, extra_params: Optional[Dict] = None - ) -> FMPCryptoPriceQueryParams: - return FMPCryptoPriceQueryParams( - symbol=query.symbol, **extra_params if extra_params else {} - ) - - @staticmethod - def extract_data( - query: FMPCryptoPriceQueryParams, credentials: Optional[Dict[str, str]] - ) -> List[FMPCryptoPriceData]: - if credentials: - api_key = credentials.get("fmp_api_key") - - url = create_url( - 3, f"historical-chart/{query.interval}/{query.symbol}", api_key - ) - return get_data_many(url, FMPCryptoPriceData) - - @staticmethod - def transform_data(data: List[FMPCryptoPriceData]) -> List[CryptoPriceData]: - return data_transformer(data, CryptoPriceData) diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/dividend_calendar.py b/openbb_sdk/providers/fmp/openbb_fmp/models/dividend_calendar.py index 878937f0f03..48d0bb0f6f4 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/dividend_calendar.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/dividend_calendar.py @@ -1,11 +1,10 @@ """FMP Dividend Calendar fetcher.""" -from datetime import datetime -from typing import Dict, List, Optional +from datetime import datetime, timedelta +from typing import Any, Dict, List, Optional from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.dividend_calendar import ( +from openbb_provider.standard_models.dividend_calendar import ( DividendCalendarData, DividendCalendarQueryParams, ) @@ -15,77 +14,76 @@ from openbb_fmp.utils.helpers import get_data_many, get_querystring class FMPDividendCalendarQueryParams(DividendCalendarQueryParams): - """FMP Dividend Calendar query. + """FMP Dividend Calendar Query. Source: https://site.financialmodelingprep.com/developer/docs/dividend-calendar-api/ The maximum time interval between the start and end date can be 3 months. Default value for time interval is 1 month. - - Parameter - --------- - start_date : date - The starting date to fetch the dividend calendar from. Default value is the - previous day from the last month. - end_date : date - The ending date to fetch the dividend calendar till. Default value is the - previous day from the current month. """ class FMPDividendCalendarData(DividendCalendarData): - """FMP Dividend Calendar data.""" + """FMP Dividend Calendar Data.""" - @validator("date", pre=True) + class Config: + fields = { + "adj_dividend": "adjDividend", + "record_date": "recordDate", + "payment_date": "paymentDate", + "declaration_date": "declarationDate", + } + + @validator("date", pre=True, check_fields=False) def date_validate(cls, v: str): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") if v else None - @validator("recordDate", pre=True) + @validator("recordDate", pre=True, check_fields=False) def record_date_validate(cls, v: str): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") if v else None - @validator("paymentDate", pre=True) + @validator("paymentDate", pre=True, check_fields=False) def payment_date_validate(cls, v: str): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") if v else None - @validator("declarationDate", pre=True) + @validator("declarationDate", pre=True, check_fields=False) def declaration_date_validate(cls, v: str): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") if v else None class FMPDividendCalendarFetcher( Fetcher[ - DividendCalendarQueryParams, - DividendCalendarData, FMPDividendCalendarQueryParams, FMPDividendCalendarData, ] ): @staticmethod - def transform_query( - query: DividendCalendarQueryParams, extra_params: Optional[Dict] = None - ) -> FMPDividendCalendarQueryParams: - return FMPDividendCalendarQueryParams( - start_date=query.start_date, - end_date=query.end_date, - **extra_params or {}, - ) + def transform_query(params: Dict[str, Any]) -> FMPDividendCalendarQueryParams: + now = datetime.now().date() + transformed_params = params + if params.get("start_date") is None: + transformed_params["start_date"] = now - timedelta(days=31) + + if params.get("end_date") is None: + transformed_params["end_date"] = now - timedelta(days=1) + return FMPDividendCalendarQueryParams(**transformed_params) @staticmethod def extract_data( - query: FMPDividendCalendarQueryParams, credentials: Optional[Dict[str, str]] + query: FMPDividendCalendarQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPDividendCalendarData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" base_url = "https://financialmodelingprep.com/api/v3" - query_str = get_querystring(query.dict(), []) + query_str = get_querystring(query.dict(by_alias=True), []) query_str = query_str.replace("start_date", "from").replace("end_date", "to") url = f"{base_url}/stock_dividend_calendar?{query_str}&apikey={api_key}" - return get_data_many(url, FMPDividendCalendarData) + return get_data_many(url, FMPDividendCalendarData, **kwargs) @staticmethod def transform_data( data: List[FMPDividendCalendarData], - ) -> List[DividendCalendarData]: - return data_transformer(data, DividendCalendarData) + ) -> List[FMPDividendCalendarData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/earnings_calendar.py b/openbb_sdk/providers/fmp/openbb_fmp/models/earnings_calendar.py index 5aaf0d5497e..56922c4270d 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/earnings_calendar.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/earnings_calendar.py @@ -1,73 +1,75 @@ """FMP Earnings Calendar fetcher.""" -from datetime import date as dateType -from typing import Dict, List, Optional +from datetime import datetime +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.earnings_calendar import ( +from openbb_provider.standard_models.earnings_calendar import ( EarningsCalendarData, EarningsCalendarQueryParams, ) +from pydantic import validator from openbb_fmp.utils.helpers import create_url, get_data_many class FMPEarningsCalendarQueryParams(EarningsCalendarQueryParams): - """FMP Earnings Calendar query. + """FMP Earnings Calendar Query. Source: https://site.financialmodelingprep.com/developer/docs/earnings-calendar-api/ - - Parameter - --------- - symbol : str - The symbol of the company. - limit : int """ -class FMPEarningsCalendarData(Data): - date: dateType - symbol: str - eps: Optional[float] - epsEstimated: Optional[float] - time: str - revenue: Optional[int] - revenueEstimated: Optional[int] - updatedFromDate: Optional[dateType] - fiscalDateEnding: dateType +class FMPEarningsCalendarData(EarningsCalendarData): + """FMP Earnings Calendar Data.""" + + class Config: + fields = { + "eps_estimated": "epsEstimated", + "revenue_estimated": "revenueEstimated", + "updated_from_date": "updatedFromDate", + "fiscal_date_ending": "fiscalDateEnding", + } + + @validator("date", pre=True, check_fields=False) + def date_validate(cls, v: str): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d") if v else None + + @validator("updatedFromDate", pre=True, check_fields=False) + def updated_from_date_validate(cls, v: str): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d") if v else None + + @validator("fiscalDateEnding", pre=True, check_fields=False) + def fiscal_date_ending_validate(cls, v: str): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d") if v else None class FMPEarningsCalendarFetcher( Fetcher[ - EarningsCalendarQueryParams, - EarningsCalendarData, FMPEarningsCalendarQueryParams, FMPEarningsCalendarData, ] ): @staticmethod - def transform_query( - query: EarningsCalendarQueryParams, extra_params: Optional[Dict] = None - ) -> FMPEarningsCalendarQueryParams: - return FMPEarningsCalendarQueryParams(symbol=query.symbol, limit=query.limit) + def transform_query(params: Dict[str, Any]) -> FMPEarningsCalendarQueryParams: + return FMPEarningsCalendarQueryParams(**params) @staticmethod def extract_data( - query: FMPEarningsCalendarQueryParams, credentials: Optional[Dict[str, str]] + query: FMPEarningsCalendarQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPEarningsCalendarData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url( 3, f"historical/earning_calendar/{query.symbol}", api_key, query, ["symbol"] ) - return get_data_many(url, FMPEarningsCalendarData) + return get_data_many(url, FMPEarningsCalendarData, **kwargs) @staticmethod def transform_data( data: List[FMPEarningsCalendarData], - ) -> List[EarningsCalendarData]: - return data_transformer(data, EarningsCalendarData) + ) -> List[FMPEarningsCalendarData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/earnings_call_transcript.py b/openbb_sdk/providers/fmp/openbb_fmp/models/earnings_call_transcript.py index 61f32eaa153..faf81eca985 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/earnings_call_transcript.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/earnings_call_transcript.py @@ -1,63 +1,56 @@ """FMP Earnings Call Transcript fetcher.""" -from typing import Dict, List, Optional +from datetime import datetime +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.base import BaseSymbol -from openbb_provider.models.earnings_call_transcript import ( +from openbb_provider.standard_models.earnings_call_transcript import ( EarningsCallTranscriptData, EarningsCallTranscriptQueryParams, ) +from pydantic import validator from openbb_fmp.utils.helpers import create_url, get_data_many -class FMPEarningsCallTranscriptQueryParams(QueryParams, BaseSymbol): - """FMP Earnings Calendar query. +class FMPEarningsCallTranscriptQueryParams(EarningsCallTranscriptQueryParams): + """FMP Earnings Calendar Query. - Source: https://site.financialmodelingprep.com/developer/docs/earnings-calendar-api/ - - Parameter - --------- - symbol : str - The symbol of the company. - year: int - The year of the transcript to get. + Source: https://site.financialmodelingprep.com/developer/docs/earning-call-transcript-api/ """ - year: int + @validator("year", pre=True, check_fields=False) + def time_validate(cls, v: int): # pylint: disable=E0213 + current_year = datetime.now().year + return current_year if v > current_year or v < 1950 else v class FMPEarningsCallTranscriptData(EarningsCallTranscriptData): - """FMP Earnings Call Transcript data.""" + """FMP Earnings Call Transcript Data.""" + + @validator("date", pre=True, check_fields=False) + def date_validate(cls, v: str): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d %H:%M:%S") class FMPEarningsCallTranscriptFetcher( Fetcher[ - EarningsCallTranscriptQueryParams, - EarningsCallTranscriptData, FMPEarningsCallTranscriptQueryParams, FMPEarningsCallTranscriptData, ] ): @staticmethod - def transform_query( - query: EarningsCallTranscriptQueryParams, extra_params: Optional[Dict] = None - ) -> FMPEarningsCallTranscriptQueryParams: - return FMPEarningsCallTranscriptQueryParams( - symbol=query.symbol, year=query.year - ) + def transform_query(params: Dict[str, Any]) -> FMPEarningsCallTranscriptQueryParams: + return FMPEarningsCallTranscriptQueryParams(**params) @staticmethod def extract_data( query: FMPEarningsCallTranscriptQueryParams, credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPEarningsCallTranscriptData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url( 4, @@ -66,10 +59,10 @@ class FMPEarningsCallTranscriptFetcher( query, ["symbol"], ) - return get_data_many(url, FMPEarningsCallTranscriptData) + return get_data_many(url, FMPEarningsCallTranscriptData, **kwargs) @staticmethod def transform_data( data: List[FMPEarningsCallTranscriptData], - ) -> List[EarningsCallTranscriptData]: - return data_transformer(data, EarningsCallTranscriptData) + ) -> List[FMPEarningsCallTranscriptData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/esg_risk_rating.py b/openbb_sdk/providers/fmp/openbb_fmp/models/esg_risk_rating.py index 113565a0396..c0fe941f08a 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/esg_risk_rating.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/esg_risk_rating.py @@ -2,13 +2,11 @@ from datetime import date -from typing import Dict, List, Literal, Optional +from typing import Any, Dict, List, Literal, Optional from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.esg_risk_rating import ( - ESGRiskRatingData, +from openbb_provider.standard_models.esg_risk_rating import ( ESGRiskRatingQueryParams, ) from pydantic import Field @@ -45,30 +43,27 @@ class FMPESGRiskRatingData(Data): class FMPESGRiskRatingFetcher( Fetcher[ - ESGRiskRatingQueryParams, - ESGRiskRatingData, FMPESGRiskRatingQueryParams, FMPESGRiskRatingData, ] ): @staticmethod - def transform_query( - query: ESGRiskRatingQueryParams, extra_params: Optional[Dict] = None - ) -> FMPESGRiskRatingQueryParams: - return FMPESGRiskRatingQueryParams.parse_obj(query) + def transform_query(params: Dict[str, Any]) -> FMPESGRiskRatingQueryParams: + return FMPESGRiskRatingQueryParams(**params) @staticmethod def extract_data( - query: FMPESGRiskRatingQueryParams, credentials: Optional[Dict[str, str]] + query: FMPESGRiskRatingQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any ) -> List[FMPESGRiskRatingData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url( 4, "esg-environmental-social-governance-data-ratings", api_key, query ) - return get_data_many(url, FMPESGRiskRatingData) + return get_data_many(url, FMPESGRiskRatingData, **kwargs) @staticmethod - def transform_data(data: List[FMPESGRiskRatingData]) -> List[ESGRiskRatingData]: - return data_transformer(data, ESGRiskRatingData) + def transform_data(data: List[FMPESGRiskRatingData]) -> List[FMPESGRiskRatingData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/esg_score.py b/openbb_sdk/providers/fmp/openbb_fmp/models/esg_score.py index a8d2fa2182b..4c4bdce552c 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/esg_score.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/esg_score.py @@ -2,12 +2,11 @@ from datetime import date, datetime -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.esg_score import ESGScoreData, ESGScoreQueryParams +from openbb_provider.standard_models.esg_score import ESGScoreQueryParams from pydantic import Field from openbb_fmp.utils.helpers import create_url, get_data_many @@ -39,25 +38,22 @@ class FMPESGScoreData(Data): url: str -class FMPESGScoreFetcher( - Fetcher[ESGScoreQueryParams, ESGScoreData, FMPESGScoreQueryParams, FMPESGScoreData] -): +class FMPESGScoreFetcher(Fetcher[FMPESGScoreQueryParams, FMPESGScoreData]): @staticmethod - def transform_query( - query: ESGScoreQueryParams, extra_params: Optional[Dict] = None - ) -> FMPESGScoreQueryParams: - return FMPESGScoreQueryParams.parse_obj(query) + def transform_query(params: Dict[str, Any]) -> FMPESGScoreQueryParams: + return FMPESGScoreQueryParams(**params) @staticmethod def extract_data( - query: FMPESGScoreQueryParams, credentials: Optional[Dict[str, str]] + query: FMPESGScoreQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any ) -> List[FMPESGScoreData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url(4, "esg-environmental-social-governance-data", api_key, query) - return get_data_many(url, FMPESGScoreData) + return get_data_many(url, FMPESGScoreData, **kwargs) @staticmethod - def transform_data(data: List[FMPESGScoreData]) -> List[ESGScoreData]: - return data_transformer(data, ESGScoreData) + def transform_data(data: List[FMPESGScoreData]) -> List[FMPESGScoreData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/esg_sector.py b/openbb_sdk/providers/fmp/openbb_fmp/models/esg_sector.py index adcfd526182..9983638b563 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/esg_sector.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/esg_sector.py @@ -2,12 +2,11 @@ from datetime import date -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data, QueryParams +from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.esg_sector import ESGSectorData, ESGSectorQueryParams +from openbb_provider.abstract.query_params import QueryParams from pydantic import Field from openbb_fmp.utils.helpers import create_url, get_data_many @@ -38,29 +37,24 @@ class FMPESGSectorData(Data): ESGScore: float = Field(alias="esg_score") -class FMPESGSectorFetcher( - Fetcher[ - ESGSectorQueryParams, ESGSectorData, FMPESGSectorQueryParams, FMPESGSectorData - ] -): +class FMPESGSectorFetcher(Fetcher[FMPESGSectorQueryParams, FMPESGSectorData]): @staticmethod - def transform_query( - query: ESGSectorQueryParams, extra_params: Optional[Dict] = None - ) -> FMPESGSectorQueryParams: - return FMPESGSectorQueryParams(year=query.year) + def transform_query(params: Dict[str, Any]) -> FMPESGSectorQueryParams: + return FMPESGSectorQueryParams(**params) @staticmethod def extract_data( - query: FMPESGSectorQueryParams, credentials: Optional[Dict[str, str]] + query: FMPESGSectorQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any ) -> List[FMPESGSectorData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url( 4, "esg-environmental-social-governance-sector-benchmark", api_key, query ) - return get_data_many(url, FMPESGSectorData) + return get_data_many(url, FMPESGSectorData, **kwargs) @staticmethod - def transform_data(data: List[FMPESGSectorData]) -> List[ESGSectorData]: - return data_transformer(data, ESGSectorData) + def transform_data(data: List[FMPESGSectorData]) -> List[FMPESGSectorData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/executive_compensation.py b/openbb_sdk/providers/fmp/openbb_fmp/models/executive_compensation.py index ded45d9daa9..817737efb96 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/executive_compensation.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/executive_compensation.py @@ -1,16 +1,11 @@ """FMP Executive Compensation Fetcher.""" -from datetime import ( - date as dateType, - datetime, -) -from typing import Dict, List, Optional +from datetime import datetime +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.executive_compensation import ( +from openbb_provider.standard_models.executive_compensation import ( ExecutiveCompensationData, ExecutiveCompensationQueryParams, ) @@ -18,72 +13,61 @@ from pydantic import validator from openbb_fmp.utils.helpers import create_url, get_data_many -# This part is only provided by FMP and not by the other providers for now. +# This endpoint is only provided by FMP and not by the other providers for now. class FMPExecutiveCompensationQueryParams(ExecutiveCompensationQueryParams): - """FMP Executive Compensation query. + """FMP Executive Compensation Query. Source: https://site.financialmodelingprep.com/developer/docs/executive-compensation-api/ - - Parameter - --------- - symbol : str - The symbol of the company. """ -class FMPExecutiveCompensationData(Data): - cik: Optional[str] - symbol: str - filingDate: dateType - acceptedDate: dateType - nameAndPosition: str - year: int - salary: float - bonus: float - stock_award: float - incentive_plan_compensation: float - all_other_compensation: float - total: float - url: str +class FMPExecutiveCompensationData(ExecutiveCompensationData): + """FMP Executive Compensation Data.""" - @validator("acceptedDate", pre=True) - def convert_accepted_date(cls, v): # pylint: disable=E0213 - return datetime.strptime(v, "%Y-%m-%d %H:%M:%S") + class Config: + fields = { + "filing_date": "filingDate", + "accepted_date": "acceptedDate", + "name_and_position": "nameAndPosition", + "stock_award": "stockAward", + "incentive_plan_compensation": "incentivePlanCompensation", + "all_other_compensation": "allOtherCompensation", + } - @validator("filingDate", pre=True) - def convert_filing_date(cls, v): # pylint: disable=E0213 + @validator("filingDate", pre=True, check_fields=False) + def filing_date_validate(cls, v): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") + @validator("acceptedDate", pre=True, check_fields=False) + def accepted_date_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d %H:%M:%S") + class FMPExecutiveCompensationFetcher( Fetcher[ # type: ignore - ExecutiveCompensationQueryParams, - ExecutiveCompensationData, FMPExecutiveCompensationQueryParams, FMPExecutiveCompensationData, ] ): @staticmethod - def transform_query( - query: ExecutiveCompensationQueryParams, extra_params: Optional[Dict] = None - ) -> FMPExecutiveCompensationQueryParams: - return FMPExecutiveCompensationQueryParams(symbol=query.symbol) + def transform_query(params: Dict[str, Any]) -> FMPExecutiveCompensationQueryParams: + return FMPExecutiveCompensationQueryParams(**params) @staticmethod def extract_data( query: FMPExecutiveCompensationQueryParams, credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPExecutiveCompensationData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url(4, "governance/executive_compensation", api_key, query) - return get_data_many(url, FMPExecutiveCompensationData) + return get_data_many(url, FMPExecutiveCompensationData, **kwargs) @staticmethod def transform_data( data: List[FMPExecutiveCompensationData], - ) -> List[ExecutiveCompensationData]: - return data_transformer(data, ExecutiveCompensationData) + ) -> List[FMPExecutiveCompensationData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/executives.py b/openbb_sdk/providers/fmp/openbb_fmp/models/executives.py deleted file mode 100644 index e61e698ce1f..00000000000 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/executives.py +++ /dev/null @@ -1,108 +0,0 @@ -"""FMP Key Executives Fetcher.""" - - -from datetime import datetime -from typing import Dict, List, Optional - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.base import BaseSymbol -from openbb_provider.models.key_executives import ( - KeyExecutivesData, - KeyExecutivesQueryParams, -) - -from openbb_fmp.utils.helpers import get_data - - -class FMPKeyExecutivesQueryParams(QueryParams, BaseSymbol): - """FMP Key Executives QueryParams. - - Source: https://financialmodelingprep.com/developer/docs/#Key-Executives - - Parameter - --------- - symbol : str - The symbol of the company. - key_executive_name : Optional[str] - The name of the key executive. - key_executive_title : Optional[str] - The title of the key executive. - key_executive_title_since : Optional[datetime] - The title since of the key executive. - key_executive_year_born : Optional[datetime] - The year born of the key executive. - key_executive_gender: Optional[str] - The gender of the key executive. - """ - - key_executive_name: Optional[str] - key_executive_title: Optional[str] - key_executive_title_since: Optional[datetime] - key_executive_year_born: Optional[datetime] - key_executive_gender: Optional[str] - - -class FMPKeyExecutivesData(Data): - name: str - title: str - titleSince: Optional[datetime] - yearBorn: Optional[datetime] - gender: Optional[str] - - -class FMPKeyExecutivesFetcher( - Fetcher[ - KeyExecutivesQueryParams, - KeyExecutivesData, - FMPKeyExecutivesQueryParams, - FMPKeyExecutivesData, - ] -): - @staticmethod - def transform_query( - query: KeyExecutivesQueryParams, extra_params: Optional[Dict] = None - ) -> FMPKeyExecutivesQueryParams: - return FMPKeyExecutivesQueryParams( - symbol=query.symbol, - key_executive_name=query.key_executive_name, - key_executive_title=query.key_executive_title, - key_executive_title_since=query.key_executive_title_since, - key_executive_year_born=query.key_executive_year_born, - key_executive_gender=query.key_executive_gender, - ) - - @staticmethod - def extract_data( - query: FMPKeyExecutivesQueryParams, credentials: Optional[Dict[str, str]] - ) -> List[FMPKeyExecutivesData]: - if credentials: - api_key = credentials.get("fmp_api_key") - - base_url = "https://financialmodelingprep.com/api/v3/" - request_url = f"{base_url}key-executives/{query.symbol}?apikey={api_key}" - data = get_data(request_url) - if isinstance(data, dict): - raise ValueError("Expected list of dicts, got dict") - - if query.key_executive_name: - data = [x for x in data if x["name"] == query.key_executive_name] - if query.key_executive_title: - data = [x for x in data if x["title"] == query.key_executive_title] - if query.key_executive_title_since: - data = [ - x for x in data if x["titleSince"] == query.key_executive_title_since - ] - if query.key_executive_year_born: - data = [x for x in data if x["yearBorn"] == query.key_executive_year_born] - if query.key_executive_gender: - data = [x for x in data if x["gender"] == query.key_executive_gender] - - return [FMPKeyExecutivesData(**d) for d in data] - - @staticmethod - def transform_data( - data: List[FMPKeyExecutivesData], - ) -> List[KeyExecutivesData]: - return data_transformer(data, KeyExecutivesData) diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/forex_eod.py b/openbb_sdk/providers/fmp/openbb_fmp/models/forex_eod.py index 8e73efb66f0..f2072f62cda 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/forex_eod.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/forex_eod.py @@ -1,80 +1,82 @@ """FMP Forex end of day fetcher.""" -from datetime import datetime -from typing import Dict, List, Optional +from datetime import datetime, timedelta +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data, QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.forex_eod import ForexEODData, ForexEODQueryParams +from openbb_provider.standard_models.forex_eod import ForexEODData, ForexEODQueryParams from pydantic import Field, validator -from openbb_fmp.utils.helpers import get_data_many +from openbb_fmp.utils.helpers import get_data_many, get_querystring -class FMPForexEODQueryParams(QueryParams): - """FMP Forex end of day query. +class FMPForexEODQueryParams(ForexEODQueryParams): + """FMP Forex end of day Query. Source: https://site.financialmodelingprep.com/developer/docs/#Historical-Forex-Price - - Parameter - --------- - symbol : str - The symbol of the company. """ - symbol: str = Field(min_length=1) +class FMPForexEODData(ForexEODData): + """FMP Forex end of day Data.""" -class FMPForexEODData(Data): - date: datetime - open: float - high: float - low: float - close: float - adjClose: float = Field(alias="adj_close") - volume: float - unadjustedVolume: float - change: float - changePercent: float - vwap: Optional[float] - label: str - changeOverTime: float + adjClose: float = Field( + description="Adjusted Close Price of the symbol.", alias="adj_close" + ) + unadjustedVolume: float = Field( + description="Unadjusted volume of the symbol.", alias="unadjusted_volume" + ) + change: float = Field( + description="Change in the price of the symbol from the previous day.", + alias="change", + ) + changePercent: float = Field( + description=r"Change \% in the price of the symbol.", alias="change_percent" + ) + label: str = Field(description="Human readable format of the date.") + changeOverTime: float = Field( + description=r"Change \% in the price of the symbol over a period of time.", + alias="change_over_time", + ) @validator("date", pre=True) - def time_validate(cls, v): # pylint: disable=E0213 + def date_validate(cls, v): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") class FMPForexEODFetcher( Fetcher[ - ForexEODQueryParams, - ForexEODData, FMPForexEODQueryParams, FMPForexEODData, ] ): @staticmethod - def transform_query( - query: ForexEODQueryParams, extra_params: Optional[Dict] = None - ) -> FMPForexEODQueryParams: - return FMPForexEODQueryParams( - symbol=query.symbol, - **extra_params or {}, - ) + def transform_query(params: Dict[str, Any]) -> FMPForexEODQueryParams: + now = datetime.now().date() + transformed_params = params + if params.get("start_date") is None: + transformed_params["start_date"] = now - timedelta(days=7) + + if params.get("end_date") is None: + transformed_params["end_date"] = now + return FMPForexEODQueryParams(**transformed_params) @staticmethod def extract_data( - query: FMPForexEODQueryParams, credentials: Optional[Dict[str, str]] + query: FMPForexEODQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPForexEODData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" base_url = "https://financialmodelingprep.com/api/v3" - url = f"{base_url}/historical-price-full/forex/{query.symbol}?&apikey={api_key}" - return get_data_many(url, FMPForexEODData, "historical") + query_str = get_querystring(query.dict(by_alias=True), ["symbol"]) + query_str = query_str.replace("start_date", "from").replace("end_date", "to") + url = f"{base_url}/historical-price-full/forex/{query.symbol}?{query_str}&apikey={api_key}" + + return get_data_many(url, FMPForexEODData, "historical", **kwargs) @staticmethod - def transform_data(data: List[FMPForexEODData]) -> List[ForexEODData]: - return data_transformer(data, ForexEODData) + def transform_data(data: List[FMPForexEODData]) -> List[FMPForexEODData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/forex_pairs.py b/openbb_sdk/providers/fmp/openbb_fmp/models/forex_pairs.py index a58519cfbdb..a00a6688bb3 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/forex_pairs.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/forex_pairs.py @@ -1,53 +1,63 @@ """FMP Forex available pairs fetcher.""" -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data, QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.forex_pairs import ForexPairsData, ForexPairsQueryParams +from openbb_provider.standard_models.forex_pairs import ( + ForexPairsData, + ForexPairsQueryParams, +) +from pydantic import Field from openbb_fmp.utils.helpers import get_data_many -class FMPForexPairsQueryParams(QueryParams): - """FMP Forex available pairs query. +class FMPForexPairsQueryParams(ForexPairsQueryParams): + """FMP Forex available pairs Query. Source: https://site.financialmodelingprep.com/developer/docs/#Historical-Forex-Price - """ -class FMPForexPairsData(Data): - """FMP Forex available pairs data.""" +class FMPForexPairsData(ForexPairsData): + """FMP Forex available pairs Data.""" + + symbol: str = Field(description="The symbol of the currency pair.") + currency: str = Field(description="The base currency of the currency pair.") + stockExchange: Optional[str] = Field( + description="The stock exchange of the currency pair.", + alias="stock_exchange", + ) + exchange_short_name: Optional[str] = Field( + description="The short name of the stock exchange of the currency pair.", + alias="exchange_short_name", + ) class FMPForexPairsFetcher( Fetcher[ - ForexPairsQueryParams, - ForexPairsData, FMPForexPairsQueryParams, FMPForexPairsData, ] ): @staticmethod - def transform_query( - query: ForexPairsQueryParams, extra_params: Optional[Dict] = None - ) -> FMPForexPairsQueryParams: - return FMPForexPairsQueryParams() + def transform_query(params: Dict[str, Any]) -> FMPForexPairsQueryParams: + return FMPForexPairsQueryParams(**params) @staticmethod def extract_data( - query: FMPForexPairsQueryParams, credentials: Optional[Dict[str, str]] + query: FMPForexPairsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPForexPairsData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" base_url = "https://financialmodelingprep.com/api/v3" url = f"{base_url}/symbol/available-forex-currency-pairs?apikey={api_key}" - return get_data_many(url, FMPForexPairsData) + + return get_data_many(url, FMPForexPairsData, **kwargs) @staticmethod - def transform_data(data: List[FMPForexPairsData]) -> List[ForexPairsData]: - return data_transformer(data, ForexPairsData) + def transform_data(data: List[FMPForexPairsData]) -> List[FMPForexPairsData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/forex_price.py b/openbb_sdk/providers/fmp/openbb_fmp/models/forex_price.py deleted file mode 100644 index 071df49b015..00000000000 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/forex_price.py +++ /dev/null @@ -1,76 +0,0 @@ -"""FMP Forex Price fetcher.""" - - -from enum import Enum -from typing import Dict, List, Literal, Optional - -from openbb_provider.abstract.data import QueryParams -from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.forex_price import ForexPriceData, ForexPriceQueryParams -from pydantic import Field - -from openbb_fmp.utils.helpers import BaseStockPriceData, create_url, get_data_many - - -class Interval(str, Enum): - oneminute = "1min" - fiveminute = "5min" - fifteenminute = "15min" - thirtyminute = "30min" - onehour = "1hour" - fourhour = "4hour" - - -class FMPForexPriceQueryParams(QueryParams): - """FMP Forex Price query. - - Source: https://site.financialmodelingprep.com/developer/docs/#Forex-Historical-Price - - Parameter - --------- - symbol : str - The symbol of the company. - interval : Enum of ["1min", "5min", "15min", "30min", "1hour", "4hour"] (default: "1hour") - The interval of the data. - """ - - symbol: str = Field(min_length=1) - interval: Literal["1min", "5min", "15min", "30min", "1hour", "4hour"] = "1hour" - - -class FMPForexPriceData(BaseStockPriceData): - """FMP Forex Price data.""" - - -class FMPForexPriceFetcher( - Fetcher[ - ForexPriceQueryParams, - ForexPriceData, - FMPForexPriceQueryParams, - FMPForexPriceData, - ] -): - @staticmethod - def transform_query( - query: ForexPriceQueryParams, extra_params: Optional[Dict] = None - ) -> FMPForexPriceQueryParams: - return FMPForexPriceQueryParams( - symbol=query.symbol, **extra_params if extra_params else {} - ) - - @staticmethod - def extract_data( - query: FMPForexPriceQueryParams, credentials: Optional[Dict[str, str]] - ) -> List[FMPForexPriceData]: - if credentials: - api_key = credentials.get("fmp_api_key") - - url = create_url( - 3, f"historical-chart/{query.interval}/{query.symbol}", api_key - ) - return get_data_many(url, FMPForexPriceData) - - @staticmethod - def transform_data(data: List[FMPForexPriceData]) -> List[ForexPriceData]: - return data_transformer(data, ForexPriceData) diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/global_news.py b/openbb_sdk/providers/fmp/openbb_fmp/models/global_news.py index 9f6125518e6..4efc3a90b5f 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/global_news.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/global_news.py @@ -1,65 +1,53 @@ """FMP Global News fetcher.""" +from typing import Any, Dict, List, Optional -from datetime import datetime -from typing import Dict, List, Optional - -from openbb_provider.abstract.data import Data, QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.global_news import GlobalNewsData, GlobalNewsQueryParams +from openbb_provider.standard_models.global_news import ( + GlobalNewsData, + GlobalNewsQueryParams, +) from pydantic import Field from openbb_fmp.utils.helpers import create_url, get_data_many -class FMPGlobalNewsQueryParams(QueryParams): +class FMPGlobalNewsQueryParams(GlobalNewsQueryParams): """FMP Global News query. Source: https://site.financialmodelingprep.com/developer/docs/general-news-api/ - - Parameter - --------- - page : int (default: 0) - The page of the data to retrieve. """ - page: int = Field(default=0) +class FMPGlobalNewsData(GlobalNewsData): + # publishedDate: datetime = Field(alias="date") + class Config: + fields = {"date": "publishedDate"} -class FMPGlobalNewsData(Data): - publishedDate: datetime = Field(alias="date") - title: str - image: str - text: str - url: str - site: str + site: str = Field(description="The site of the news.") class FMPGlobalNewsFetcher( Fetcher[ - GlobalNewsQueryParams, - GlobalNewsData, FMPGlobalNewsQueryParams, FMPGlobalNewsData, ] ): @staticmethod - def transform_query( - query: GlobalNewsQueryParams, extra_params: Optional[Dict] = None - ) -> FMPGlobalNewsQueryParams: - return FMPGlobalNewsQueryParams(page=query.page) + def transform_query(params: Dict[str, Any]) -> FMPGlobalNewsQueryParams: + return FMPGlobalNewsQueryParams(**params) @staticmethod def extract_data( - query: FMPGlobalNewsQueryParams, credentials: Optional[Dict[str, str]] + query: FMPGlobalNewsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any ) -> List[FMPGlobalNewsData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url(4, "general_news", api_key, query) - return get_data_many(url, FMPGlobalNewsData) + return get_data_many(url, FMPGlobalNewsData, **kwargs) @staticmethod - def transform_data(data: List[FMPGlobalNewsData]) -> List[GlobalNewsData]: - return data_transformer(data, GlobalNewsData) + def transform_data(data: List[FMPGlobalNewsData]) -> List[FMPGlobalNewsData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/historical_dividends.py b/openbb_sdk/providers/fmp/openbb_fmp/models/historical_dividends.py index ae5bed51d20..591f76ea61f 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/historical_dividends.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/historical_dividends.py @@ -1,15 +1,10 @@ """FMP Historical Dividends fetcher.""" -from datetime import ( - date as dateType, - datetime, -) -from typing import Dict, List, Optional +from datetime import datetime +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.historical_dividends import ( +from openbb_provider.standard_models.historical_dividends import ( HistoricalDividendsData, HistoricalDividendsQueryParams, ) @@ -22,69 +17,58 @@ class FMPHistoricalDividendsQueryParams(HistoricalDividendsQueryParams): """FMP Historical Dividends query. Source: https://site.financialmodelingprep.com/developer/docs/#Historical-Dividends - - Parameter - --------- - symbol : str - The symbol of the company. """ -class FMPHistoricalDividendsData(Data): +class FMPHistoricalDividendsData(HistoricalDividendsData): """FMP Historical Dividends data.""" - date: dateType - label: str - adjDividend: float - dividend: float - recordDate: Optional[dateType] - paymentDate: Optional[dateType] - declarationDate: Optional[dateType] + class Config: + fields = { + "adj_dividend": "adjDividend", + "record_date": "recordDate", + "payment_date": "paymentDate", + "declaration_date": "declarationDate", + } - @validator("declarationDate", pre=True) + @validator("declarationDate", pre=True, check_fields=False) def declaration_date_validate(cls, v: str): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") if v else None - @validator("recordDate", pre=True) + @validator("recordDate", pre=True, check_fields=False) def record_date_validate(cls, v: str): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") if v else None - @validator("paymentDate", pre=True) + @validator("paymentDate", pre=True, check_fields=False) def payment_date_validate(cls, v: str): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") if v else None class FMPHistoricalDividendsFetcher( Fetcher[ - HistoricalDividendsQueryParams, - HistoricalDividendsData, FMPHistoricalDividendsQueryParams, FMPHistoricalDividendsData, ] ): @staticmethod - def transform_query( - query: HistoricalDividendsQueryParams, extra_params: Optional[Dict] = None - ) -> FMPHistoricalDividendsQueryParams: - return FMPHistoricalDividendsQueryParams( - symbol=query.symbol, - **extra_params or {}, - ) + def transform_query(params: Dict[str, Any]) -> FMPHistoricalDividendsQueryParams: + return FMPHistoricalDividendsQueryParams(**params) @staticmethod def extract_data( - query: FMPHistoricalDividendsQueryParams, credentials: Optional[Dict[str, str]] + query: FMPHistoricalDividendsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPHistoricalDividendsData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url( 3, f"historical-price-full/stock_dividend/{query.symbol}", api_key ) - return get_data_many(url, FMPHistoricalDividendsData, "historical") + return get_data_many(url, FMPHistoricalDividendsData, "historical", **kwargs) @staticmethod def transform_data( data: List[FMPHistoricalDividendsData], - ) -> List[HistoricalDividendsData]: - return data_transformer(data, HistoricalDividendsData) + ) -> List[FMPHistoricalDividendsData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/historical_employees.py b/openbb_sdk/providers/fmp/openbb_fmp/models/historical_employees.py index 3c31ab47b4d..837dd47119b 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/historical_employees.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/historical_employees.py @@ -1,17 +1,15 @@ """FMP Historical Employees fetcher.""" -from datetime import date, datetime -from typing import Dict, List, Optional +from datetime import datetime +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.historical_employees import ( +from openbb_provider.standard_models.historical_employees import ( HistoricalEmployeesData, HistoricalEmployeesQueryParams, ) -from pydantic import Field, validator +from pydantic import validator from openbb_fmp.utils.helpers import create_url, get_data_many @@ -20,68 +18,58 @@ class FMPHistoricalEmployeesQueryParams(HistoricalEmployeesQueryParams): """FMP Historical Employees query. Source: https://site.financialmodelingprep.com/developer/docs/historical-numer-of-employees-api/ - - Parameter - --------- - symbol : str - The symbol of the company. """ -class FMPHistoricalEmployeesData(Data): - """FMP Historical Employees data.""" +class FMPHistoricalEmployeesData(HistoricalEmployeesData): + """FMP Historical Employees Data.""" - symbol: str = Field(min_length=1) - cik: int - acceptanceTime: datetime = Field(alias="acceptance_time") - periodOfReport: date = Field(alias="period_of_report") - companyName: str = Field(alias="company_name") - formType: str = Field(alias="form_type") - filingDate: date = Field(alias="filing_date") - employeeCount: int = Field(alias="employee_count") - source: str + class Config: + fields = { + "acceptance_time": "acceptanceTime", + "period_of_report": "periodOfReport", + "company_name": "companyName", + "form_type": "formType", + "filing_date": "filingDate", + "employee_count": "employeeCount", + } - @validator("acceptanceTime", pre=True) + @validator("acceptanceTime", pre=True, check_fields=False) def acceptance_time_validate(cls, v): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d %H:%M:%S") - @validator("periodOfReport", pre=True) + @validator("periodOfReport", pre=True, check_fields=False) def period_of_report_validate(cls, v): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") - @validator("filingDate", pre=True) + @validator("filingDate", pre=True, check_fields=False) def filing_date_validate(cls, v): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") class FMPHistoricalEmployeesFetcher( Fetcher[ - HistoricalEmployeesQueryParams, - HistoricalEmployeesData, FMPHistoricalEmployeesQueryParams, FMPHistoricalEmployeesData, ] ): @staticmethod - def transform_query( - query: HistoricalEmployeesQueryParams, extra_params: Optional[Dict] = None - ) -> FMPHistoricalEmployeesQueryParams: - return FMPHistoricalEmployeesQueryParams( - symbol=query.symbol, **extra_params or {} - ) + def transform_query(params: Dict[str, Any]) -> FMPHistoricalEmployeesQueryParams: + return FMPHistoricalEmployeesQueryParams(**params) @staticmethod def extract_data( - query: FMPHistoricalEmployeesQueryParams, credentials: Optional[Dict[str, str]] + query: FMPHistoricalEmployeesQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any ) -> List[FMPHistoricalEmployeesData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url(4, "historical/employee_count", api_key, query) - return get_data_many(url, FMPHistoricalEmployeesData) + return get_data_many(url, FMPHistoricalEmployeesData, **kwargs) @staticmethod def transform_data( data: List[FMPHistoricalEmployeesData], - ) -> List[HistoricalEmployeesData]: - return data_transformer(data, HistoricalEmployeesData) + ) -> List[FMPHistoricalEmployeesData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/historical_stock_splits.py b/openbb_sdk/providers/fmp/openbb_fmp/models/historical_stock_splits.py index 3dadfc6afb2..eaa7833b62d 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/historical_stock_splits.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/historical_stock_splits.py @@ -1,63 +1,59 @@ """FMP Historical Stock Splits fetcher.""" -from typing import Dict, List, Optional +from datetime import datetime +from typing import Any, Dict, List, Optional from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.historical_stock_splits import ( +from openbb_provider.standard_models.historical_stock_splits import ( HistoricalStockSplitsData, HistoricalStockSplitsQueryParams, ) +from pydantic import validator from openbb_fmp.utils.helpers import create_url, get_data_many class FMPHistoricalStockSplitsQueryParams(HistoricalStockSplitsQueryParams): - """FMP Historical Stock Splits query. + """FMP Historical Stock Splits Query. Source: https://site.financialmodelingprep.com/developer/docs/historical-stock-splits-api/ - - Parameter - --------- - symbol : str - The symbol of the company. """ class FMPHistoricalStockSplitsData(HistoricalStockSplitsData): - """FMP Historical Stock Splits data.""" + """FMP Historical Stock Splits Data.""" + + @validator("date", pre=True, check_fields=False) + def date_validate(cls, v: str): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d") if v else None class FMPHistoricalStockSplitsFetcher( Fetcher[ - HistoricalStockSplitsQueryParams, - HistoricalStockSplitsData, FMPHistoricalStockSplitsQueryParams, FMPHistoricalStockSplitsData, ] ): @staticmethod - def transform_query( - query: HistoricalStockSplitsQueryParams, extra_params: Optional[Dict] = None - ) -> FMPHistoricalStockSplitsQueryParams: - return FMPHistoricalStockSplitsQueryParams(symbol=query.symbol) + def transform_query(params: Dict[str, Any]) -> FMPHistoricalStockSplitsQueryParams: + return FMPHistoricalStockSplitsQueryParams(**params) @staticmethod def extract_data( query: FMPHistoricalStockSplitsQueryParams, credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPHistoricalStockSplitsData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url( 3, f"historical-price-full/stock_split/{query.symbol}", api_key ) - return get_data_many(url, FMPHistoricalStockSplitsData, "historical") + return get_data_many(url, FMPHistoricalStockSplitsData, "historical", **kwargs) @staticmethod def transform_data( data: List[FMPHistoricalStockSplitsData], - ) -> List[HistoricalStockSplitsData]: - return data_transformer(data, HistoricalStockSplitsData) + ) -> List[FMPHistoricalStockSplitsData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/income_statement.py b/openbb_sdk/providers/fmp/openbb_fmp/models/income_statement.py index 6ba49a8252f..0c37cd02b31 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/income_statement.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/income_statement.py @@ -1,47 +1,31 @@ """FMP Income Statement Fetcher.""" -from datetime import ( - date as dateType, - datetime, -) -from typing import Dict, List, Literal, Optional +from typing import Any, Dict, List, Literal, Optional -from openbb_provider.abstract.data import Data, QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.income_statement import ( +from openbb_provider.standard_models.income_statement import ( IncomeStatementData, IncomeStatementQueryParams, ) -from pydantic import Field, NonNegativeInt, root_validator +from pydantic import Field, root_validator -from openbb_fmp.utils.helpers import create_url, get_data_many +from openbb_fmp.utils.helpers import get_data_many PeriodType = Literal["annual", "quarter"] -class FMPIncomeStatementQueryParams(QueryParams): +class FMPIncomeStatementQueryParams(IncomeStatementQueryParams): """FMP Income Statement QueryParams. Source: https://financialmodelingprep.com/developer/docs/#Income-Statement - Parameter - --------- - symbol : Optional[str] - The symbol of the company if no CIK is provided. - cik : Optional[str] - The CIK of the company if no symbol is provided. - period : Literal["annual", "quarter"] - The period of the income statement. Default is "annual". - limit : Optional[NonNegativeInt] - The limit of the income statement. + Either a Symbol or CIK is required. Symbol is preferred over CIK. """ - symbol: Optional[str] - cik: Optional[str] - period: PeriodType = Field(default="annual") - limit: Optional[NonNegativeInt] + cik: Optional[str] = Field( + description="The CIK of the company if no symbol is provided." + ) @root_validator() def check_symbol_or_cik(cls, values): # pylint: disable=no-self-argument @@ -50,82 +34,73 @@ class FMPIncomeStatementQueryParams(QueryParams): return values -class FMPIncomeStatementData(Data): - date: dateType - symbol: str - reportedCurrency: str = Field(alias="currency") - cik: Optional[int] - fillingDate: Optional[dateType] = Field(alias="filing_date") - acceptedDate: Optional[datetime] +class FMPIncomeStatementData(IncomeStatementData): + class Config: + fields = { + "currency": "reportedCurrency", + "filing_date": "fillingDate", + "accepted_date": "acceptedDate", + "cost_of_revenue": "costOfRevenue", + "gross_profit": "grossProfit", + "research_and_development_expenses": "researchAndDevelopmentExpenses", + "general_and_administrative_expenses": "generalAndAdministrativeExpenses", + "selling_and_marketing_expenses": "sellingAndMarketingExpenses", + "selling_general_and_administrative_expenses": "sellingGeneralAndAdministrativeExpenses", + "other_expenses": "otherExpenses", + "operating_expenses": "operatingExpenses", + "depreciation_and_amortization": "depreciationAndAmortization", + "operating_income": "operatingIncome", + "interest_income": "interestIncome", + "interest_expense": "interestExpense", + "total_other_income_expenses_net": "totalOtherIncomeExpensesNet", + "income_before_tax": "incomeBeforeTax", + "income_tax_expense": "incomeTaxExpense", + "net_income": "netIncome", + "eps_diluted": "epsdiluted", + "weighted_average_shares_outstanding": "weightedAverageShsOut", + "weighted_average_shares_outstanding_dil": "weightedAverageShsOutDil", + } + calendarYear: Optional[int] - period: Optional[str] - revenue: Optional[int] - costOfRevenue: Optional[int] - grossProfit: Optional[int] grossProfitRatio: Optional[float] - researchAndDevelopmentExpenses: Optional[int] - generalAndAdministrativeExpenses: Optional[int] - sellingAndMarketingExpenses: Optional[int] - sellingGeneralAndAdministrativeExpenses: Optional[int] - otherExpenses: Optional[int] - operatingExpenses: Optional[int] - costAndExpenses: Optional[int] - interestIncome: Optional[int] - interestExpense: Optional[int] - depreciationAndAmortization: Optional[int] - ebitda: Optional[int] ebitdaratio: Optional[float] - operatingIncome: Optional[int] operatingIncomeRatio: Optional[float] - totalOtherIncomeExpensesNet: Optional[int] - incomeBeforeTax: Optional[int] incomeBeforeTaxRatio: Optional[float] - incomeTaxExpense: Optional[int] - netIncome: Optional[int] netIncomeRatio: Optional[float] - eps: Optional[float] - epsdiluted: Optional[float] = Field(alias="eps_diluted") - weightedAverageShsOut: Optional[int] = Field( - alias="weighted_average_shares_outstanding" - ) - weightedAverageShsOutDil: Optional[int] = Field( - alias="weighted_average_shares_outstanding_dil" - ) link: Optional[str] finalLink: Optional[str] class FMPIncomeStatementFetcher( Fetcher[ - IncomeStatementQueryParams, - IncomeStatementData, FMPIncomeStatementQueryParams, FMPIncomeStatementData, ] ): @staticmethod - def transform_query( - query: IncomeStatementQueryParams, extra_params: Optional[Dict] = None - ) -> FMPIncomeStatementQueryParams: - period: PeriodType = "annual" if query.period == "annually" else "quarter" - return FMPIncomeStatementQueryParams( - symbol=query.symbol, period=period, **extra_params if extra_params else {} - ) + def transform_query(params: Dict[str, Any]) -> FMPIncomeStatementQueryParams: + return FMPIncomeStatementQueryParams(**params) @staticmethod def extract_data( - query: FMPIncomeStatementQueryParams, credentials: Optional[Dict[str, str]] + query: FMPIncomeStatementQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPIncomeStatementData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" - url = create_url( - 3, f"income-statement/{query.symbol}", api_key, query, ["symbol"] + symbol = query.symbol or query.cik + base_url = "https://financialmodelingprep.com/api/v3" + + url = ( + f"{base_url}/income-statement/{symbol}?" + f"period={query.period}&limit={query.limit}&apikey={api_key}" ) - return get_data_many(url, FMPIncomeStatementData) + + return get_data_many(url, FMPIncomeStatementData, **kwargs) @staticmethod def transform_data( data: List[FMPIncomeStatementData], - ) -> List[IncomeStatementData]: - return data_transformer(data, IncomeStatementData) + ) -> List[FMPIncomeStatementData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/income_statement_growth.py b/openbb_sdk/providers/fmp/openbb_fmp/models/income_statement_growth.py new file mode 100644 index 00000000000..325635a24f9 --- /dev/null +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/income_statement_growth.py @@ -0,0 +1,91 @@ +"""FMP Income Statement Growth Fetcher.""" + + +from datetime import datetime +from typing import Any, Dict, List, Optional + +from openbb_provider.abstract.fetcher import Fetcher +from openbb_provider.standard_models.income_statement_growth import ( + IncomeStatementGrowthData, + IncomeStatementGrowthQueryParams, +) +from pydantic import validator + +from openbb_fmp.utils.helpers import create_url, get_data_many + + +class FMPIncomeStatementGrowthQueryParams(IncomeStatementGrowthQueryParams): + """FMP Income Statement Growth QueryParams. + + Source: https://site.financialmodelingprep.com/developer/docs/financial-statements-growth-api/ + """ + + +class FMPIncomeStatementGrowthData(IncomeStatementGrowthData): + """FMP Income Statement Growth Data.""" + + class Config: + fields = { + "growth_revenue": "growthRevenue", + "growth_cost_of_revenue": "growthCostOfRevenue", + "growth_gross_profit": "growthGrossProfit", + "growth_gross_profit_ratio": "growthGrossProfitRatio", + "growth_research_and_development_expenses": "growthResearchAndDevelopmentExpenses", + "growth_general_and_administrative_expenses": "growthGeneralAndAdministrativeExpenses", + "growth_selling_and_marketing_expenses": "growthSellingAndMarketingExpenses", + "growth_other_expenses": "growthOtherExpenses", + "growth_operating_expenses": "growthOperatingExpenses", + "growth_cost_and_expenses": "growthCostAndExpenses", + "growth_interest_expense": "growthInterestExpense", + "growth_depreciation_and_amortization": "growthDepreciationAndAmortization", + "growth_ebitda": "growthEBITDA", + "growth_ebitda_ratio": "growthEBITDARatio", + "growth_operating_income": "growthOperatingIncome", + "growth_operating_income_ratio": "growthOperatingIncomeRatio", + "growth_total_other_income_expenses_net": "growthTotalOtherIncomeExpensesNet", + "growth_income_before_tax": "growthIncomeBeforeTax", + "growth_income_before_tax_ratio": "growthIncomeBeforeTaxRatio", + "growth_income_tax_expense": "growthIncomeTaxExpense", + "growth_net_income": "growthNetIncome", + "growth_net_income_ratio": "growthNetIncomeRatio", + "growth_eps": "growthEPS", + "growth_eps_diluted": "growthEPSDiluted", + "growth_weighted_average_shs_out": "growthWeightedAverageShsOut", + "growth_weighted_average_shs_out_dil": "growthWeightedAverageShsOutDil", + } + + @validator("date", pre=True, check_fields=False) + def date_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d") + + +class FMPIncomeStatementGrowthFetcher( + Fetcher[ + FMPIncomeStatementGrowthQueryParams, + FMPIncomeStatementGrowthData, + ] +): + @staticmethod + def transform_query(params: Dict[str, Any]) -> FMPIncomeStatementGrowthQueryParams: + return FMPIncomeStatementGrowthQueryParams(**params) + + @staticmethod + def extract_data( + query: FMPIncomeStatementGrowthQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> List[FMPIncomeStatementGrowthData]: + api_key = credentials.get("fmp_api_key") if credentials else "" + + query.period = "annual" if query.period == "annually" else "quarter" + + url = create_url( + 3, f"income-statement-growth/{query.symbol}", api_key, query, ["symbol"] + ) + return get_data_many(url, FMPIncomeStatementGrowthData, **kwargs) + + @staticmethod + def transform_data( + data: List[FMPIncomeStatementGrowthData], + ) -> List[FMPIncomeStatementGrowthData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/institutional_ownership.py b/openbb_sdk/providers/fmp/openbb_fmp/models/institutional_ownership.py index d0ca366cbaf..5ab2d41f14b 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/institutional_ownership.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/institutional_ownership.py @@ -1,119 +1,94 @@ """FMP Institutional Ownership Fetcher.""" -from datetime import ( - date as dateType, - datetime, -) -from typing import Dict, List, Optional +from datetime import datetime +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data, QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.base import BaseSymbol -from openbb_provider.models.institutional_ownership import ( +from openbb_provider.standard_models.institutional_ownership import ( InstitutionalOwnershipData, InstitutionalOwnershipQueryParams, ) -from pydantic import Field, validator +from pydantic import validator from openbb_fmp.utils.helpers import create_url, get_data_many -class FMPInstitutionalOwnershipQueryParams(QueryParams, BaseSymbol): - """FMP Institutional Ownership QueryParams. +class FMPInstitutionalOwnershipQueryParams(InstitutionalOwnershipQueryParams): + """FMP Institutional Ownership Query. Source: https://site.financialmodelingprep.com/developer/docs/institutional-stock-ownership-api/ - - Parameter - --------- - symbol : str - The symbol of the company if cik is not provided. - include_current_quarter : bool - Whether to include the current quarter. Default is False. - date : Optional[dateType] - A specific date to get data for. """ - includeCurrentQuarter: bool = Field(default=False, alias="include_current_quarter") - date: Optional[dateType] +class FMPInstitutionalOwnershipData(InstitutionalOwnershipData): + """FMP Institutional Ownership Data.""" -class FMPInstitutionalOwnershipData(Data): - symbol: str = Field(min_length=1) - cik: str = Field(min_length=1) - date: dateType - investorsHolding: int - lastInvestorsHolding: int - investorsHoldingChange: int - numberOf13Fshares: int - lastNumberOf13Fshares: int - numberOf13FsharesChange: int - totalInvested: float - lastTotalInvested: float - totalInvestedChange: float - ownershipPercent: float - lastOwnershipPercent: float - ownershipPercentChange: float - newPositions: int - lastNewPositions: int - newPositionsChange: int - increasedPositions: int - lastIncreasedPositions: int - increasedPositionsChange: int - closedPositions: int - lastClosedPositions: int - closedPositionsChange: int - reducedPositions: int - lastReducedPositions: int - reducedPositionsChange: int - totalCalls: int - lastTotalCalls: int - totalCallsChange: int - totalPuts: int - lastTotalPuts: int - totalPutsChange: int - putCallRatio: float - lastPutCallRatio: float - putCallRatioChange: float + class Config: + fields = { + "investors_holding": "investorsHolding", + "last_investors_holding": "lastInvestorsHolding", + "investors_holding_change": "investorsHoldingChange", + "number_of_13f_shares": "numberOf13Fshares", + "last_number_of_13f_shares": "lastNumberOf13Fshares", + "number_of_13f_shares_change": "numberOf13FsharesChange", + "total_invested": "totalInvested", + "last_total_invested": "lastTotalInvested", + "total_invested_change": "totalInvestedChange", + "ownership_percent": "ownershipPercent", + "last_ownership_percent": "lastOwnershipPercent", + "ownership_percent_change": "ownershipPercentChange", + "new_positions": "newPositions", + "last_new_positions": "lastNewPositions", + "new_positions_change": "newPositionsChange", + "increased_positions": "increasedPositions", + "last_increased_positions": "lastIncreasedPositions", + "increased_positions_change": "increasedPositionsChange", + "closed_positions": "closedPositions", + "last_closed_positions": "lastClosedPositions", + "closed_positions_change": "closedPositionsChange", + "reduced_positions": "reducedPositions", + "last_reduced_positions": "lastReducedPositions", + "reduced_positions_change": "reducedPositionsChange", + "total_calls": "totalCalls", + "last_total_calls": "lastTotalCalls", + "total_calls_change": "totalCallsChange", + "total_puts": "totalPuts", + "last_total_puts": "lastTotalPuts", + "total_puts_change": "totalPutsChange", + "put_call_ratio": "putCallRatio", + "last_put_call_ratio": "lastPutCallRatio", + "put_call_ratio_change": "putCallRatioChange", + } - @validator("date", pre=True) + @validator("date", pre=True, check_fields=False) def time_validate(cls, v): # pylint: disable=no-self-argument return datetime.strptime(v, "%Y-%m-%d") class FMPInstitutionalOwnershipFetcher( Fetcher[ - InstitutionalOwnershipQueryParams, - InstitutionalOwnershipData, FMPInstitutionalOwnershipQueryParams, FMPInstitutionalOwnershipData, ] ): @staticmethod - def transform_query( - query: InstitutionalOwnershipQueryParams, extra_params: Optional[Dict] = None - ) -> FMPInstitutionalOwnershipQueryParams: - return FMPInstitutionalOwnershipQueryParams( - symbol=query.symbol, - date=query.date, - include_current_quarter=query.include_current_quarter, - **extra_params if extra_params else {} - ) + def transform_query(params: Dict[str, Any]) -> FMPInstitutionalOwnershipQueryParams: + return FMPInstitutionalOwnershipQueryParams(**params) @staticmethod def extract_data( query: FMPInstitutionalOwnershipQueryParams, credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPInstitutionalOwnershipData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url(4, "institutional-ownership/symbol-ownership", api_key, query) - return get_data_many(url, FMPInstitutionalOwnershipData) + return get_data_many(url, FMPInstitutionalOwnershipData, **kwargs) @staticmethod def transform_data( data: List[FMPInstitutionalOwnershipData], - ) -> List[InstitutionalOwnershipData]: - return data_transformer(data, InstitutionalOwnershipData) + ) -> List[FMPInstitutionalOwnershipData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/key_executives.py b/openbb_sdk/providers/fmp/openbb_fmp/models/key_executives.py new file mode 100644 index 00000000000..4bed893c319 --- /dev/null +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/key_executives.py @@ -0,0 +1,67 @@ +"""FMP Key Executives Fetcher.""" + + +from datetime import datetime +from typing import Any, Dict, List, Optional + +from openbb_provider.abstract.fetcher import Fetcher +from openbb_provider.standard_models.key_executives import ( + KeyExecutivesData, + KeyExecutivesQueryParams, +) +from pydantic import validator + +from openbb_fmp.utils.helpers import get_data_many + + +class FMPKeyExecutivesQueryParams(KeyExecutivesQueryParams): + """FMP Key Executives QueryParams. + + Source: https://financialmodelingprep.com/developer/docs/#Key-Executives + """ + + +class FMPKeyExecutivesData(KeyExecutivesData): + """FMP Key Executives Data.""" + + class Config: + fields = { + "currency_pay": "currencyPay", + "year_born": "yearBorn", + "title_since": "titleSince", + } + + @validator("titleSince", pre=True, check_fields=False) + def time_validate(cls, v): # pylint: disable=E0213 + return datetime.fromtimestamp(v / 1000) + + +class FMPKeyExecutivesFetcher( + Fetcher[ + FMPKeyExecutivesQueryParams, + FMPKeyExecutivesData, + ] +): + @staticmethod + def transform_query(params: Dict[str, Any]) -> FMPKeyExecutivesQueryParams: + return FMPKeyExecutivesQueryParams(**params) + + @staticmethod + def extract_data( + query: FMPKeyExecutivesQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> List[FMPKeyExecutivesData]: + api_key = credentials.get("fmp_api_key") if credentials else "" + + base_url = "https://financialmodelingprep.com/api/v3" + url = f"{base_url}/key-executives/{query.symbol}?apikey={api_key}" + + return get_data_many(url, FMPKeyExecutivesData, **kwargs) + + @staticmethod + def transform_data( + data: List[FMPKeyExecutivesData], + ) -> List[FMPKeyExecutivesData]: + return data + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/key_metrics.py b/openbb_sdk/providers/fmp/openbb_fmp/models/key_metrics.py index c22b98201dc..45b6848a7b4 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/key_metrics.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/key_metrics.py @@ -1,135 +1,118 @@ """Key Metrics fetcher.""" -from datetime import date as dateType -from typing import Dict, List, Literal, Optional +from datetime import datetime +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data, QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.key_metrics import KeyMetricsData, KeyMetricsQueryParams -from pydantic import Field +from openbb_provider.standard_models.key_metrics import ( + KeyMetricsData, + KeyMetricsQueryParams, +) +from pydantic import validator from openbb_fmp.utils.helpers import create_url, get_data_many -class FMPKeyMetricsQueryParams(QueryParams): - """FMP Key Metrics QueryParams. +class FMPKeyMetricsQueryParams(KeyMetricsQueryParams): + """FMP Key Metrics Query. Source: https://site.financialmodelingprep.com/developer/docs/company-key-metrics-api/ - - Parameter - --------- - symbol : str - The symbol of the company. - period : Literal["quarter", "annual"] - The period of the key metrics. Default is "annual". - limit : Optional[int] - The limit of the key metrics to be returned. """ - symbol: str - period: Literal["quarter", "annual"] = "annual" - limit: Optional[int] = None +class FMPKeyMetricsData(KeyMetricsData): + """FMP Key Metrics Data.""" -class FMPKeyMetricsData(Data): - symbol: str - date: dateType - period: str - revenuePerShare: float - netIncomePerShare: float - operatingCashFlowPerShare: Optional[float] - freeCashFlowPerShare: Optional[float] - cashPerShare: Optional[float] - bookValuePerShare: Optional[float] - tangibleBookValuePerShare: Optional[float] - shareholdersEquityPerShare: Optional[float] - interestDebtPerShare: Optional[float] - marketCap: Optional[float] - enterpriseValue: Optional[float] - peRatio: Optional[float] - priceToSalesRatio: Optional[float] - pocfratio: Optional[float] = Field(alias="pocf_ratio") - pfcfRatio: Optional[float] - pbRatio: Optional[float] - ptbRatio: Optional[float] - evToSales: Optional[float] - enterpriseValueOverEBITDA: Optional[float] = Field( - alias="enterprise_value_over_ebitda" - ) - evToOperatingCashFlow: Optional[float] - evToFreeCashFlow: Optional[float] - earningsYield: Optional[float] - freeCashFlowYield: Optional[Optional[float]] - debtToEquity: Optional[float] - debtToAssets: Optional[float] - netDebtToEBITDA: Optional[float] = Field(alias="net_debt_to_ebitda") - currentRatio: Optional[float] - interestCoverage: Optional[float] - incomeQuality: Optional[float] - dividendYield: Optional[Optional[float]] - payoutRatio: Optional[Optional[float]] - salesGeneralAndAdministrativeToRevenue: Optional[float] - researchAndDdevelopementToRevenue: Optional[float] = Field( - alias="research_and_developement_to_revenue" - ) - intangiblesToTotalAssets: Optional[float] - capexToOperatingCashFlow: Optional[float] - capexToRevenue: Optional[float] - capexToDepreciation: Optional[float] - stockBasedCompensationToRevenue: Optional[float] - grahamNumber: Optional[float] - roic: Optional[float] - returnOnTangibleAssets: Optional[float] - grahamNetNet: Optional[float] - workingCapital: Optional[float] - tangibleAssetValue: Optional[float] - netCurrentAssetValue: Optional[float] - investedCapital: Optional[float] - averageReceivables: Optional[float] - averagePayables: Optional[float] - averageInventory: Optional[float] - daysSalesOutstanding: Optional[float] - daysPayablesOutstanding: Optional[float] - daysOfInventoryOnHand: Optional[float] - receivablesTurnover: Optional[float] - payablesTurnover: Optional[float] - inventoryTurnover: Optional[float] - roe: Optional[float] - capexPerShare: Optional[float] + class Config: + fields = { + "revenue_per_share": "revenuePerShare", + "net_income_per_share": "netIncomePerShare", + "operating_cash_flow_per_share": "operatingCashFlowPerShare", + "free_cash_flow_per_share": "freeCashFlowPerShare", + "cash_per_share": "cashPerShare", + "book_value_per_share": "bookValuePerShare", + "tangible_book_value_per_share": "tangibleBookValuePerShare", + "shareholders_equity_per_share": "shareholdersEquityPerShare", + "interest_debt_per_share": "interestDebtPerShare", + "market_cap": "marketCap", + "enterprise_value": "enterpriseValue", + "pe_ratio": "peRatio", + "price_to_sales_ratio": "priceToSalesRatio", + "pocf_ratio": "pocfratio", + "pfcf_ratio": "pfcfRatio", + "pb_ratio": "pbRatio", + "ptb_ratio": "ptbRatio", + "ev_to_sales": "evToSales", + "enterprise_value_over_ebitda": "enterpriseValueOverEBITDA", + "ev_to_operating_cash_flow": "evToOperatingCashFlow", + "ev_to_free_cash_flow": "evToFreeCashFlow", + "earnings_yield": "earningsYield", + "free_cash_flow_yield": "freeCashFlowYield", + "debt_to_equity": "debtToEquity", + "debt_to_assets": "debtToAssets", + "net_debt_to_ebitda": "netDebtToEBITDA", + "current_ratio": "currentRatio", + "interest_coverage": "interestCoverage", + "income_quality": "incomeQuality", + "dividend_yield": "dividendYield", + "payout_ratio": "payoutRatio", + "sales_general_and_administrative_to_revenue": "salesGeneralAndAdministrativeToRevenue", + "research_and_development_to_revenue": "researchAndDdevelopementToRevenue", + "intangibles_to_total_assets": "intangiblesToTotalAssets", + "capex_to_operating_cash_flow": "capexToOperatingCashFlow", + "capex_to_revenue": "capexToRevenue", + "capex_to_depreciation": "capexToDepreciation", + "stock_based_compensation_to_revenue": "stockBasedCompensationToRevenue", + "graham_number": "grahamNumber", + "return_on_tangible_assets": "returnOnTangibleAssets", + "graham_net_net": "grahamNetNet", + "working_capital": "workingCapital", + "tangible_asset_value": "tangibleAssetValue", + "net_current_asset_value": "netCurrentAssetValue", + "invested_capital": "investedCapital", + "average_receivables": "averageReceivables", + "average_payables": "averagePayables", + "average_inventory": "averageInventory", + "days_sales_outstanding": "daysSalesOutstanding", + "days_payables_outstanding": "daysPayablesOutstanding", + "days_of_inventory_on_hand": "daysOfInventoryOnHand", + "receivables_turnover": "receivablesTurnover", + "payables_turnover": "payablesTurnover", + "inventory_turnover": "inventoryTurnover", + "capex_per_share": "capexPerShare", + } + + @validator("date", pre=True, check_fields=False) + def date_validate(cls, v): # pylint: disable=no-self-argument + return datetime.strptime(v, "%Y-%m-%d") class FMPKeyMetricsFetcher( Fetcher[ - KeyMetricsQueryParams, - KeyMetricsData, FMPKeyMetricsQueryParams, FMPKeyMetricsData, ] ): @staticmethod - def transform_query( - query: KeyMetricsQueryParams, extra_params: Optional[Dict] = None - ) -> FMPKeyMetricsQueryParams: - return FMPKeyMetricsQueryParams( - symbol=query.symbol, - limit=query.limit, - period="annual" if query.period == "annually" else "quarter", - ) + def transform_query(params: Dict[str, Any]) -> FMPKeyMetricsQueryParams: + return FMPKeyMetricsQueryParams(**params) @staticmethod def extract_data( - query: FMPKeyMetricsQueryParams, credentials: Optional[Dict[str, str]] + query: FMPKeyMetricsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPKeyMetricsData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" + + query.period = "annual" if query.period == "annually" else "quarter" url = create_url( 3, f"key-metrics/{query.symbol}", api_key, query, exclude=["symbol"] ) - return get_data_many(url, FMPKeyMetricsData) + return get_data_many(url, FMPKeyMetricsData, **kwargs) @staticmethod - def transform_data(data: List[FMPKeyMetricsData]) -> List[KeyMetricsData]: - return data_transformer(data, KeyMetricsData) + def transform_data(data: List[FMPKeyMetricsData]) -> List[FMPKeyMetricsData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/major_indices_constituents.py b/openbb_sdk/providers/fmp/openbb_fmp/models/major_indices_constituents.py index 61ad3ee37ca..adcf4a2cade 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/major_indices_constituents.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/major_indices_constituents.py @@ -2,11 +2,10 @@ from datetime import datetime -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.major_indices_constituents import ( +from openbb_provider.standard_models.major_indices_constituents import ( MajorIndicesConstituentsData, MajorIndicesConstituentsQueryParams, ) @@ -21,18 +20,20 @@ class FMPMajorIndicesConstituentsQueryParams(MajorIndicesConstituentsQueryParams Source: https://site.financialmodelingprep.com/developer/docs/list-of-dow-companies-api/ https://site.financialmodelingprep.com/developer/docs/list-of-sp-500-companies-api/ https://site.financialmodelingprep.com/developer/docs/list-of-nasdaq-companies-api/ - - Parameter - --------- - index : Literal['nasdaq', 'sp500', 'dowjones'] - The index for which we want to fetch the constituents. Default is 'dowjones'. """ class FMPMajorIndicesConstituentsData(MajorIndicesConstituentsData): """FMP Major Indices Constituents data.""" - @validator("dateFirstAdded", pre=True) + class Config: + fields = { + "sub_sector": "subSector", + "headquarter": "headQuarter", + "date_first_added": "dateFirstAdded", + } + + @validator("dateFirstAdded", pre=True, check_fields=False) def date_first_added_validate(cls, v): # pylint: disable=E0213 try: return datetime.strptime(v, "%Y-%m-%d") if v else None @@ -40,7 +41,7 @@ class FMPMajorIndicesConstituentsData(MajorIndicesConstituentsData): # For returning string in case of mismatched dates return v - @validator("founded", pre=True) + @validator("founded", pre=True, check_fields=False) def founded_validate(cls, v): # pylint: disable=E0213 try: return datetime.strptime(v, "%Y-%m-%d") if v else None @@ -51,31 +52,31 @@ class FMPMajorIndicesConstituentsData(MajorIndicesConstituentsData): class FMPMajorIndicesConstituentsFetcher( Fetcher[ - MajorIndicesConstituentsQueryParams, - MajorIndicesConstituentsData, FMPMajorIndicesConstituentsQueryParams, FMPMajorIndicesConstituentsData, ] ): @staticmethod def transform_query( - query: MajorIndicesConstituentsQueryParams, extra_params: Optional[Dict] = None + params: Dict[str, Any] ) -> FMPMajorIndicesConstituentsQueryParams: - return FMPMajorIndicesConstituentsQueryParams( - index=query.index, - **extra_params or {}, - ) + return FMPMajorIndicesConstituentsQueryParams(**params) @staticmethod def extract_data( - query: FMPMajorIndicesConstituentsQueryParams, api_key: str + query: FMPMajorIndicesConstituentsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPMajorIndicesConstituentsData]: + api_key = credentials.get("fmp_api_key") if credentials else "" + base_url = "https://financialmodelingprep.com/api/v3" url = f"{base_url}/{query.index}_constituent/?apikey={api_key}" - return get_data_many(url, FMPMajorIndicesConstituentsData) + + return get_data_many(url, FMPMajorIndicesConstituentsData, **kwargs) @staticmethod def transform_data( data: List[FMPMajorIndicesConstituentsData], - ) -> List[MajorIndicesConstituentsData]: - return data_transformer(data, MajorIndicesConstituentsData) + ) -> List[FMPMajorIndicesConstituentsData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/major_indices_eod.py b/openbb_sdk/providers/fmp/openbb_fmp/models/major_indices_eod.py index 7a9b1384ade..0da6b86e9bd 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/major_indices_eod.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/major_indices_eod.py @@ -1,100 +1,88 @@ """FMP Major Indices end of day fetcher.""" -from datetime import date, datetime -from typing import Dict, List, Literal, Optional +from datetime import datetime +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data, QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer, get_querystring -from openbb_provider.models.major_indices_eod import ( +from openbb_provider.standard_models.major_indices_eod import ( MajorIndicesEODData, MajorIndicesEODQueryParams, ) +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS +from openbb_provider.utils.helpers import get_querystring from pydantic import Field, NonNegativeInt, validator from openbb_fmp.utils.helpers import get_data_many -class FMPMajorIndicesEODQueryParams(QueryParams): - """FMP Major Indices end of day query. +class FMPMajorIndicesEODQueryParams(MajorIndicesEODQueryParams): + """FMP Major Indices end of day Query. - Source: https://site.financialmodelingprep.com/developer/docs/#Historical-stock-index-prices - - Parameter - --------- - symbol : str - The symbol of the company. - start_date : date - The start date of the stock data from which to retrieve the data. - end_date : date - The end date of the stock data up to which to retrieve the data. - timeseries : Optional[int] - The number of days to look back. - serietype : Optional[Literal["line"]] - The type of the series. Only "line" is supported. + Source: https://site.financialmodelingprep.com/developer/docs/historical-index-price-api/ """ - symbol: str = Field(min_length=1) - serietype: Optional[Literal["line"]] - start_date: date - end_date: date - timeseries: Optional[NonNegativeInt] # Number of days to looks back + timeseries: Optional[NonNegativeInt] = Field( + default=None, description="Number of days to look back." + ) -class FMPMajorIndicesEODData(Data): - date: datetime - open: float - high: float - low: float - close: float - adjClose: float = Field(alias="adj_close") - volume: float - unadjustedVolume: float - change: float - changePercent: float - vwap: float - label: str - changeOverTime: float +class FMPMajorIndicesEODData(MajorIndicesEODData): + """FMP Major Indices end of day Data.""" + + date: datetime = Field(description=DATA_DESCRIPTIONS.get("date", "")) + adjClose: float = Field( + description="Adjusted Close Price of the symbol.", alias="adj_close" + ) + unadjustedVolume: float = Field( + description="Unadjusted volume of the symbol.", alias="unadjusted_volume" + ) + change: float = Field( + description="Change in the price of the symbol from the previous day.", + alias="change", + ) + changePercent: float = Field( + description=r"Change \% in the price of the symbol.", alias="change_percent" + ) + vwap: float = Field(description="Volume Weighted Average Price of the symbol.") + label: str = Field(description="Human readable format of the date.") + changeOverTime: float = Field( + description=r"Change \% in the price of the symbol over a period of time.", + alias="change_over_time", + ) @validator("date", pre=True) - def time_validate(cls, v): # pylint: disable=E0213 + def date_validate(cls, v): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") class FMPMajorIndicesEODFetcher( Fetcher[ - MajorIndicesEODQueryParams, - MajorIndicesEODData, FMPMajorIndicesEODQueryParams, FMPMajorIndicesEODData, ] ): @staticmethod - def transform_query( - query: MajorIndicesEODQueryParams, extra_params: Optional[Dict] = None - ) -> FMPMajorIndicesEODQueryParams: - raw_end = query.end_date if query.end_date else datetime.now() - return FMPMajorIndicesEODQueryParams( - symbol=query.symbol, - start_date=query.start_date, - end_date=raw_end, - **extra_params if extra_params else {}, - ) + def transform_query(params: Dict[str, Any]) -> FMPMajorIndicesEODQueryParams: + return FMPMajorIndicesEODQueryParams(**params) @staticmethod def extract_data( - query: FMPMajorIndicesEODQueryParams, credentials: Optional[Dict[str, str]] + query: FMPMajorIndicesEODQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPMajorIndicesEODData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" - base_url = "https://financialmodelingprep.com/api/v3/" - query_str = get_querystring(query.dict(), ["symbol"]) + base_url = "https://financialmodelingprep.com/api/v3" + query_str = get_querystring(query.dict(by_alias=True), ["symbol"]) query_str = query_str.replace("start_date", "from").replace("end_date", "to") - url = f"{base_url}historical-price-full/{query.symbol}?{query_str}&apikey={api_key}" - return get_data_many(url, FMPMajorIndicesEODData, "historical") + url = f"{base_url}/historical-price-full/index/%5E{query.symbol}?{query_str}&apikey={api_key}" + + return get_data_many(url, FMPMajorIndicesEODData, "historical", **kwargs) @staticmethod - def transform_data(data: List[FMPMajorIndicesEODData]) -> List[MajorIndicesEODData]: - return data_transformer(data, MajorIndicesEODData) + def transform_data( + data: List[FMPMajorIndicesEODData], + ) -> List[FMPMajorIndicesEODData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/major_indices_price.py b/openbb_sdk/providers/fmp/openbb_fmp/models/major_indices_price.py deleted file mode 100644 index ba77b099d45..00000000000 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/major_indices_price.py +++ /dev/null @@ -1,74 +0,0 @@ -"""FMP Major Indices Price fetcher.""" - - -from typing import Dict, List, Literal, Optional - -from openbb_provider.abstract.data import QueryParams -from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.base import BaseSymbol -from openbb_provider.models.major_indices_price import ( - MajorIndicesPriceData, - MajorIndicesPriceQueryParams, -) -from pydantic import Field - -from openbb_fmp.utils.helpers import BaseStockPriceData, create_url, get_data_many - - -class FMPMajorIndicesPriceQueryParams(QueryParams, BaseSymbol): - """FMP MajorIndices Price query. - - Source: https://site.financialmodelingprep.com/developer/docs/#Historical-stock-index-prices - - Parameter - --------- - symbol : str - The symbol of the index. - interval : Literal['1min', '5min', '15min', '30min', '1hour', '4hour'] - The interval of the index data to fetch. Default is '1hour`. - """ - - interval: Literal["1min", "5min", "15min", "30min", "1hour", "4hour"] = Field( - default="1hour" - ) - - -class FMPMajorIndicesPriceData(BaseStockPriceData): - """FMP MajorIndices Price data.""" - - -class FMPMajorIndicesPriceFetcher( - Fetcher[ - MajorIndicesPriceQueryParams, - MajorIndicesPriceData, - FMPMajorIndicesPriceQueryParams, - FMPMajorIndicesPriceData, - ] -): - @staticmethod - def transform_query( - query: MajorIndicesPriceQueryParams, extra_params: Optional[Dict] = None - ) -> FMPMajorIndicesPriceQueryParams: - return FMPMajorIndicesPriceQueryParams( - symbol=query.symbol, - **extra_params or {}, - ) - - @staticmethod - def extract_data( - query: FMPMajorIndicesPriceQueryParams, credentials: Optional[Dict[str, str]] - ) -> List[FMPMajorIndicesPriceData]: - if credentials: - api_key = credentials.get("fmp_api_key") - - url = create_url( - 3, f"historical-chart/{query.interval}/%5E{query.symbol}", api_key - ) - return get_data_many(url, FMPMajorIndicesPriceData) - - @staticmethod - def transform_data( - data: List[FMPMajorIndicesPriceData], - ) -> List[MajorIndicesPriceData]: - return data_transformer(data, MajorIndicesPriceData) diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/price_target.py b/openbb_sdk/providers/fmp/openbb_fmp/models/price_target.py index 95b39a435ac..624f490c01f 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/price_target.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/price_target.py @@ -2,67 +2,69 @@ from datetime import datetime -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.price_target import PriceTargetData, PriceTargetQueryParams -from pydantic import Field +from openbb_provider.standard_models.price_target import ( + PriceTargetData, + PriceTargetQueryParams, +) +from pydantic import validator from openbb_fmp.utils.helpers import create_url, get_data_many class FMPPriceTargetQueryParams(PriceTargetQueryParams): - """FMP Price Target query. + """FMP Price Target Query. Source: https://site.financialmodelingprep.com/developer/docs/#Price-Target - - Parameter - --------- - symbol : str - The symbol of the company. """ -class FMPPriceTargetData(Data): - symbol: str - publishedDate: datetime - newsURL: str = Field(alias="news_url") - newsTitle: Optional[str] - analystName: Optional[str] - priceTarget: float - adjPriceTarget: float - priceWhenPosted: float - newsPublisher: str - newsBaseURL: str = Field(alias="news_base_url") - analystCompany: str +class FMPPriceTargetData(PriceTargetData): + """FMP Price Target Data.""" + + class Config: + fields = { + "published_date": "publishedDate", + "news_url": "newsURL", + "news_title": "newsTitle", + "analyst_name": "analystName", + "price_target": "priceTarget", + "adj_price_target": "adjPriceTarget", + "price_when_posted": "priceWhenPosted", + "news_publisher": "newsPublisher", + "news_base_url": "newsBaseURL", + "analyst_company": "analystCompany", + } + + @validator("publishedDate", pre=True, check_fields=False) + def published_date_validate(cls, v: str): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d") if v else None class FMPPriceTargetFetcher( Fetcher[ - PriceTargetQueryParams, - PriceTargetData, FMPPriceTargetQueryParams, FMPPriceTargetData, ] ): @staticmethod - def transform_query( - query: PriceTargetQueryParams, extra_params: Optional[Dict] = None - ) -> FMPPriceTargetQueryParams: - return FMPPriceTargetQueryParams.parse_obj(query) + def transform_query(params: Dict[str, Any]) -> FMPPriceTargetQueryParams: + return FMPPriceTargetQueryParams(**params) @staticmethod def extract_data( - query: FMPPriceTargetQueryParams, credentials: Optional[Dict[str, str]] + query: FMPPriceTargetQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any ) -> List[FMPPriceTargetData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url(4, "price-target", api_key, query) - return get_data_many(url, FMPPriceTargetData) + + return get_data_many(url, FMPPriceTargetData, **kwargs) @staticmethod - def transform_data(data: List[FMPPriceTargetData]) -> List[PriceTargetData]: - return data_transformer(data, PriceTargetData) + def transform_data(data: List[FMPPriceTargetData]) -> List[FMPPriceTargetData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/price_target_consensus.py b/openbb_sdk/providers/fmp/openbb_fmp/models/price_target_consensus.py index 486da7cf6f8..8aee5dfec8c 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/price_target_consensus.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/price_target_consensus.py @@ -1,13 +1,10 @@ """FMP Price Target Consensus fetcher.""" -from enum import Enum -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.price_target_consensus import ( +from openbb_provider.standard_models.price_target_consensus import ( PriceTargetConsensusData, PriceTargetConsensusQueryParams, ) @@ -15,62 +12,48 @@ from openbb_provider.models.price_target_consensus import ( from openbb_fmp.utils.helpers import create_url, get_data_many -class Interval(str, Enum): - oneminute = "1min" - fiveminute = "5min" - fifteenminute = "15min" - thirtyminute = "30min" - onehour = "1hour" - fourhour = "4hour" - day = "day" - - class FMPPriceTargetConsensusQueryParams(PriceTargetConsensusQueryParams): - """FMP Price Target Consensus query. + """FMP Price Target Consensus Query. Source: https://site.financialmodelingprep.com/developer/docs/price-target-consensus-api/ - - Parameter - --------- - symbol : str - The symbol of the company. """ -class FMPPriceTargetConsensusData(Data): - symbol: str - targetHigh: float - targetLow: float - targetConsensus: float - targetMedian: float +class FMPPriceTargetConsensusData(PriceTargetConsensusData): + """FMP Price Target Consensus Data.""" + + class Config: + fields = { + "target_high": "targetHigh", + "target_low": "targetLow", + "target_consensus": "targetConsensus", + "target_median": "targetMedian", + } class FMPPriceTargetConsensusFetcher( Fetcher[ - PriceTargetConsensusQueryParams, - PriceTargetConsensusData, FMPPriceTargetConsensusQueryParams, FMPPriceTargetConsensusData, ] ): @staticmethod - def transform_query( - query: PriceTargetConsensusQueryParams, extra_params: Optional[Dict] = None - ) -> FMPPriceTargetConsensusQueryParams: - return FMPPriceTargetConsensusQueryParams(symbol=query.symbol) + def transform_query(params: Dict[str, Any]) -> FMPPriceTargetConsensusQueryParams: + return FMPPriceTargetConsensusQueryParams(**params) @staticmethod def extract_data( - query: FMPPriceTargetConsensusQueryParams, credentials: Optional[Dict[str, str]] + query: FMPPriceTargetConsensusQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any ) -> List[FMPPriceTargetConsensusData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url(4, "price-target-consensus", api_key, query) - return get_data_many(url, FMPPriceTargetConsensusData) + return get_data_many(url, FMPPriceTargetConsensusData, **kwargs) @staticmethod def transform_data( data: List[FMPPriceTargetConsensusData], - ) -> List[PriceTargetConsensusData]: - return data_transformer(data, PriceTargetConsensusData) + ) -> List[FMPPriceTargetConsensusData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/revenue_business_line.py b/openbb_sdk/providers/fmp/openbb_fmp/models/revenue_business_line.py index 5552ef2204c..a1648bee37b 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/revenue_business_line.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/revenue_business_line.py @@ -2,11 +2,10 @@ from datetime import datetime -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.revenue_business_line import ( +from openbb_provider.standard_models.revenue_business_line import ( RevenueBusinessLineData, RevenueBusinessLineQueryParams, ) @@ -14,64 +13,50 @@ from pydantic import validator from openbb_fmp.utils.helpers import create_url, get_data -# This part is only provided by FMP and not by the other providers for now. - class FMPRevenueBusinessLineQueryParams(RevenueBusinessLineQueryParams): - """FMP Revenue Business Line QueryParams. + """FMP Revenue Business Line Query. Source: https://site.financialmodelingprep.com/developer/docs/sales-revenue-by-segments-api/ - - Parameter - --------- - symbol : Optional[str] - The symbol of the company if no CIK is provided. - period : Literal["annual", "quarterly"] - The period of the income statement. Default is "annual". - structure : Literal["hierarchical", "flat"] - The structure of the revenue business line. Default is "flat". """ class FMPRevenueBusinessLineData(RevenueBusinessLineData): - @validator("date", pre=True) - def time_validate(cls, v): # pylint: disable=E0213 + """FMP Revenue Business Line Data.""" + + @validator("date", pre=True, check_fields=False) + def date_validate(cls, v): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") class FMPRevenueBusinessLineFetcher( Fetcher[ # type: ignore - RevenueBusinessLineQueryParams, - RevenueBusinessLineData, FMPRevenueBusinessLineQueryParams, FMPRevenueBusinessLineData, ] ): @staticmethod - def transform_query( - query: RevenueBusinessLineQueryParams, extra_params: Optional[Dict] = None - ) -> FMPRevenueBusinessLineQueryParams: - return FMPRevenueBusinessLineQueryParams.parse_obj(query) + def transform_query(params: Dict[str, Any]) -> FMPRevenueBusinessLineQueryParams: + return FMPRevenueBusinessLineQueryParams(**params) @staticmethod def extract_data( - query: FMPRevenueBusinessLineQueryParams, credentials: Optional[Dict[str, str]] + query: FMPRevenueBusinessLineQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any ) -> List[FMPRevenueBusinessLineData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" + + query.period = "annual" if query.period == "annually" else "quarter" - # Type has to be ignored below because we are using something different than the literal type - query.period = "quarter" if query.period == "quarterly" else "annual" # type: ignore url = create_url(4, "revenue-product-segmentation", api_key, query) - data = get_data(url) + data = get_data(url, **kwargs) + if isinstance(data, dict): raise ValueError("Expected list of dicts, got dict") data = [ - { - "date": list(d.keys())[0], - "data_and_service": list(d.values())[0], - } + {"date": list(d.keys())[0], "business_line": list(d.values())[0]} for d in data ] @@ -80,5 +65,5 @@ class FMPRevenueBusinessLineFetcher( @staticmethod def transform_data( data: List[FMPRevenueBusinessLineData], - ) -> List[RevenueBusinessLineData]: - return data_transformer(data, RevenueBusinessLineData) + ) -> List[FMPRevenueBusinessLineData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/revenue_geographic.py b/openbb_sdk/providers/fmp/openbb_fmp/models/revenue_geographic.py index 60a09e59533..b630f1a056b 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/revenue_geographic.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/revenue_geographic.py @@ -2,11 +2,10 @@ from datetime import datetime -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.revenue_geographic import ( +from openbb_provider.standard_models.revenue_geographic import ( RevenueGeographicData, RevenueGeographicQueryParams, ) @@ -14,80 +13,63 @@ from pydantic import validator from openbb_fmp.utils.helpers import create_url, get_data -# This part is only provided by FMP and not by the other providers for now. - class FMPRevenueGeographicQueryParams(RevenueGeographicQueryParams): """FMP Revenue Geographic QueryParams. - Source: https://financialmodelingprep.com/developer/docs/#Revenue-Geographic-Segmentation - - Parameter - --------- - symbol : Optional[str] - The symbol of the company if no CIK is provided. - period : Literal["annual", "quarterly"] - The period of the income statement. Default is "annual". - structure : Literal["hierarchical", "flat"] - The structure of the revenue geographic. Default is "flat". + Source: https://site.financialmodelingprep.com/developer/docs/revenue-geographic-by-segments-api/ """ class FMPRevenueGeographicData(RevenueGeographicData): + """FMP Revenue Geographic Data.""" + @validator("date", pre=True) - def time_validate(cls, v): # pylint: disable=E0213 + def date_validate(cls, v): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") class FMPRevenueGeographicFetcher( Fetcher[ # type: ignore - RevenueGeographicQueryParams, - RevenueGeographicData, FMPRevenueGeographicQueryParams, FMPRevenueGeographicData, ] ): @staticmethod - def transform_query( - query: RevenueGeographicQueryParams, extra_params: Optional[Dict] = None - ) -> FMPRevenueGeographicQueryParams: - return FMPRevenueGeographicQueryParams( - symbol=query.symbol, period=query.period, structure=query.structure - ) + def transform_query(params: Dict[str, Any]) -> FMPRevenueGeographicQueryParams: + return FMPRevenueGeographicQueryParams(**params) @staticmethod def extract_data( - query: FMPRevenueGeographicQueryParams, credentials: Optional[Dict[str, str]] + query: FMPRevenueGeographicQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any ) -> List[FMPRevenueGeographicData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" + + query.period = "annual" if query.period == "annually" else "quarter" - query.period = "quarter" if query.period == "quarterly" else "annual" # type: ignore url = create_url(4, "revenue-geographic-segmentation", api_key, query) - data = get_data(url) + data = get_data(url, **kwargs) + if isinstance(data, dict): raise ValueError("Expected list of dicts, got dict") - data = [ - {**v, "date": k} for d in data for k, v in d.items() if isinstance(v, dict) - ] - data = [ { - "date": d.get("date"), - "americas": d.get("Americas Segment") - or d.get("North America Segment") - or d.get("Americas") - or d.get("North America"), - "europe": d.get("Europe Segment") or d.get("Europe"), - "greater_china": d.get("Greater China Segment") - or d.get("Greater China"), - "japan": d.get("Japan Segment") or d.get("Japan") or d.get("J P"), - "rest_of_asia_pacific": d.get("Rest of Asia Pacific Segment") - or d.get("Rest of Asia Pacific") - or d.get("Asia-Pacific"), + **v, + "date": k, + "americas": v.get("Americas Segment", v.get("North America")), + "europe": v.get("Europe Segment", v.get("Europe")), + "greater_china": v.get("Greater China Segment", v.get("Greater China")), + "japan": v.get("Japan Segment", v.get("Japan", v.get("J P"))), + "rest_of_asia_pacific": v.get( + "Rest of Asia Pacific Segment", v.get("Asia-Pacific") + ), } for d in data + for k, v in d.items() + if isinstance(v, dict) ] return [FMPRevenueGeographicData(**d) for d in data] @@ -95,6 +77,5 @@ class FMPRevenueGeographicFetcher( @staticmethod def transform_data( data: List[FMPRevenueGeographicData], - ) -> List[RevenueGeographicData]: - # Parse data to RevenueGeographicData - return data_transformer(data, RevenueGeographicData) + ) -> List[FMPRevenueGeographicData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/risk_premium.py b/openbb_sdk/providers/fmp/openbb_fmp/models/risk_premium.py index d8053471019..e9733c3c513 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/risk_premium.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/risk_premium.py @@ -1,11 +1,13 @@ """FMP Risk Premium fetcher.""" -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.risk_premium import RiskPremiumData, RiskPremiumQueryParams +from openbb_provider.standard_models.risk_premium import ( + RiskPremiumData, + RiskPremiumQueryParams, +) from openbb_fmp.utils.helpers import create_url, get_data_many @@ -14,35 +16,41 @@ class FMPRiskPremiumQueryParams(RiskPremiumQueryParams): """FMP Risk Premium query. Source: https://site.financialmodelingprep.com/developer/docs/market-risk-premium-api/ - """ class FMPRiskPremiumData(RiskPremiumData): """FMP Risk Premium data.""" + class Config: + fields = { + "total_equity_risk_premium": "totalEquityRiskPremium", + "country_risk_premium": "countryRiskPremium", + } + class FMPRiskPremiumFetcher( Fetcher[ - RiskPremiumQueryParams, - RiskPremiumData, FMPRiskPremiumQueryParams, FMPRiskPremiumData, ] ): @staticmethod - def transform_query( - query: RiskPremiumQueryParams, extra_params: Optional[Dict] = None - ) -> FMPRiskPremiumQueryParams: - return FMPRiskPremiumQueryParams() + def transform_query(params: Dict[str, Any]) -> FMPRiskPremiumQueryParams: + return FMPRiskPremiumQueryParams(**params) @staticmethod def extract_data( - query: FMPRiskPremiumQueryParams, api_key: str + query: FMPRiskPremiumQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any ) -> List[FMPRiskPremiumData]: + api_key = credentials.get("fmp_api_key") if credentials else "" + url = create_url(4, "market_risk_premium", api_key) - return get_data_many(url, FMPRiskPremiumData) + + return get_data_many(url, FMPRiskPremiumData, **kwargs) @staticmethod - def transform_data(data: List[FMPRiskPremiumData]) -> List[RiskPremiumData]: - return data_transformer(data, RiskPremiumData) + def transform_data(data: List[FMPRiskPremiumData]) -> List[FMPRiskPremiumData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/sec_filings.py b/openbb_sdk/providers/fmp/openbb_fmp/models/sec_filings.py index 122dfa07fc9..2d64de6da6d 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/sec_filings.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/sec_filings.py @@ -1,81 +1,65 @@ """SEC Filings fetcher.""" -from datetime import ( - date as dateType, - datetime, -) -from typing import Dict, List, Optional +from datetime import datetime +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.sec_filings import SECFilingsData, SECFilingsQueryParams +from openbb_provider.standard_models.sec_filings import ( + SECFilingsData, + SECFilingsQueryParams, +) from pydantic import validator from openbb_fmp.utils.helpers import create_url, get_data_many class FMPSECFilingsQueryParams(SECFilingsQueryParams): - """FMP SEC Filings QueryParams. + """FMP SEC Filings Query. Source: https://site.financialmodelingprep.com/developer/docs/sec-filings-api/ - - Parameter - --------- - symbol : str - The symbol of the company. - type : str - The type of the SEC filing form. (full list: https://www.sec.gov/forms) - page : int - The page of the results. - limit : int - The limit of the results. """ -class FMPSECFilingsData(Data): - symbol: str - fillingDate: dateType - acceptedDate: dateType - cik: str - type: str - link: str - finalLink: str +class FMPSECFilingsData(SECFilingsData): + """FMP SEC Filings Data.""" - @validator("fillingDate", "acceptedDate", pre=True) + class Config: + fields = { + "filling_date": "fillingDate", + "accepted_date": "acceptedDate", + "final_link": "finalLink", + } + + @validator("fillingDate", "acceptedDate", pre=True, check_fields=False) def convert_date(cls, v): # pylint: disable=no-self-argument - return datetime.strptime(v, "%Y-%m-%d %H:%M:%S").date() + return datetime.strptime(v, "%Y-%m-%d %H:%M:%S") class FMPSECFilingsFetcher( Fetcher[ - SECFilingsQueryParams, - SECFilingsData, FMPSECFilingsQueryParams, FMPSECFilingsData, ] ): @staticmethod - def transform_query( - query: SECFilingsQueryParams, extra_params: Optional[Dict] = None - ) -> FMPSECFilingsQueryParams: - return FMPSECFilingsQueryParams( - symbol=query.symbol, page=query.page, limit=query.limit, type=query.type - ) + def transform_query(params: Dict[str, Any]) -> FMPSECFilingsQueryParams: + return FMPSECFilingsQueryParams(**params) @staticmethod def extract_data( - query: FMPSECFilingsQueryParams, credentials: Optional[Dict[str, str]] + query: FMPSECFilingsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPSECFilingsData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url( 3, f"sec_filings/{query.symbol}", api_key, query, exclude=["symbol"] ) - return get_data_many(url, FMPSECFilingsData) + + return get_data_many(url, FMPSECFilingsData, **kwargs) @staticmethod - def transform_data(data: List[FMPSECFilingsData]) -> List[SECFilingsData]: - return data_transformer(data, SECFilingsData) + def transform_data(data: List[FMPSECFilingsData]) -> List[FMPSECFilingsData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/share_statistics.py b/openbb_sdk/providers/fmp/openbb_fmp/models/share_statistics.py index b12961215d4..e7702fffb9e 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/share_statistics.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/share_statistics.py @@ -1,17 +1,11 @@ """FMP Share Statistics Fetcher.""" -from datetime import ( - date as dateType, - datetime, -) -from typing import Dict, List, Optional +from datetime import datetime +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data, QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.base import BaseSymbol -from openbb_provider.models.share_statistics import ( +from openbb_provider.standard_models.share_statistics import ( ShareStatisticsData, ShareStatisticsQueryParams, ) @@ -20,57 +14,52 @@ from pydantic import validator from openbb_fmp.utils.helpers import create_url, get_data_many -class FMPShareStatisticsQueryParams(QueryParams, BaseSymbol): +class FMPShareStatisticsQueryParams(ShareStatisticsQueryParams): """FMP Income Statement QueryParams. Source: https://site.financialmodelingprep.com/developer/docs/shares-float-api/ - - Parameter - --------- - symbol : str - The symbol of the company. """ -class FMPShareStatisticsData(Data): - symbol: str - date: dateType - freeFloat: float - floatShares: float - outstandingShares: float - source: str +class FMPShareStatisticsData(ShareStatisticsData): + """FMP Share Statistics Data.""" + + class Config: + fields = { + "free_float": "freeFloat", + "float_shares": "floatShares", + "outstanding_shares": "outstandingShares", + } @validator("date", pre=True) - def time_validate(cls, v): # pylint: disable=E0213 + def date_validate(cls, v): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d %H:%M:%S") class FMPShareStatisticsFetcher( Fetcher[ - ShareStatisticsQueryParams, - ShareStatisticsData, FMPShareStatisticsQueryParams, FMPShareStatisticsData, ] ): @staticmethod - def transform_query( - query: ShareStatisticsQueryParams, extra_params: Optional[Dict] = None - ) -> FMPShareStatisticsQueryParams: - return FMPShareStatisticsQueryParams(symbol=query.symbol) + def transform_query(params: Dict[str, Any]) -> FMPShareStatisticsQueryParams: + return FMPShareStatisticsQueryParams(**params) @staticmethod def extract_data( - query: FMPShareStatisticsQueryParams, credentials: Optional[Dict[str, str]] + query: FMPShareStatisticsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any ) -> List[FMPShareStatisticsData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url(4, "shares_float", api_key, query) - return get_data_many(url, FMPShareStatisticsData) + + return get_data_many(url, FMPShareStatisticsData, **kwargs) @staticmethod def transform_data( data: List[FMPShareStatisticsData], - ) -> List[ShareStatisticsData]: - return data_transformer(data, ShareStatisticsData) + ) -> List[FMPShareStatisticsData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_eod.py b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_eod.py index add57ce8114..be451be672e 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_eod.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_eod.py @@ -2,91 +2,88 @@ from datetime import datetime, timedelta -from typing import Dict, List, Literal, Optional +from typing import Any, Dict, List, Optional from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer, get_querystring -from openbb_provider.models.stock_eod import StockEODData, StockEODQueryParams +from openbb_provider.standard_models.stock_eod import StockEODData, StockEODQueryParams +from openbb_provider.utils.helpers import get_querystring from pydantic import Field, NonNegativeInt, validator from openbb_fmp.utils.helpers import get_data_many class FMPStockEODQueryParams(StockEODQueryParams): - """FMP Stock end of day query. + """FMP Stock end of day Query. Source: https://financialmodelingprep.com/developer/docs/#Stock-Historical-Price - - Parameter - --------- - symbol : str - The symbol of the company. - start_date : date - The start date of the stock data from which to retrieve the data. - end_date : date - The end date of the stock data up to which to retrieve the data. - timeseries : Optional[int] - The number of days to look back. - series_type : Optional[Literal["line"]] - The type of the series. Only "line" is supported. """ - # symbol: str = Field(min_length=1) - series_type: Optional[Literal["line"]] - # start_date: date - # end_date: date - timeseries: Optional[NonNegativeInt] # Number of days to looks back + timeseries: Optional[NonNegativeInt] = Field( + default=None, description="Number of days to look back." + ) class FMPStockEODData(StockEODData): - adjClose: float = Field(alias="adj_close") - unadjustedVolume: float - change: float - changePercent: float - vwap: float - label: str - changeOverTime: float + """FMP Stock end of day Data.""" - @validator("date", pre=True) - def time_validate(cls, v): # pylint: disable=E0213 + adjClose: float = Field( + description="Adjusted Close Price of the symbol.", alias="adj_close" + ) + unadjustedVolume: float = Field( + description="Unadjusted volume of the symbol.", alias="unadjusted_volume" + ) + change: float = Field( + description="Change in the price of the symbol from the previous day.", + alias="change", + ) + changePercent: float = Field( + description=r"Change \% in the price of the symbol.", alias="change_percent" + ) + + label: str = Field(description="Human readable format of the date.") + changeOverTime: float = Field( + description=r"Change \% in the price of the symbol over a period of time.", + alias="change_over_time", + ) + + @validator("date", pre=True, check_fields=False) + def date_validate(cls, v): # pylint: disable=E0213 return datetime.strptime(v, "%Y-%m-%d") class FMPStockEODFetcher( Fetcher[ - StockEODQueryParams, - StockEODData, FMPStockEODQueryParams, FMPStockEODData, ] ): @staticmethod - def transform_query( - query: StockEODQueryParams, extra_params: Optional[Dict] = None - ) -> FMPStockEODQueryParams: - now = datetime.now() - start_date = query.start_date or (now - timedelta(days=5)) - end_date = query.end_date or now - return FMPStockEODQueryParams( - symbol=query.symbol, - start_date=start_date, - end_date=end_date, - **extra_params or {}, - ) + def transform_query(params: Dict[str, Any]) -> FMPStockEODQueryParams: + now = datetime.now().date() + transformed_params = params + if params.get("start_date") is None: + transformed_params["start_date"] = now - timedelta(days=7) + + if params.get("end_date") is None: + transformed_params["end_date"] = now + + return FMPStockEODQueryParams(**transformed_params) @staticmethod def extract_data( - query: FMPStockEODQueryParams, credentials: Optional[Dict[str, str]] + query: FMPStockEODQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPStockEODData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" base_url = "https://financialmodelingprep.com/api/v3" - query_str = get_querystring(query.dict(), ["symbol"]) + query_str = get_querystring(query.dict(by_alias=True), ["symbol"]) query_str = query_str.replace("start_date", "from").replace("end_date", "to") url = f"{base_url}/historical-price-full/{query.symbol}?{query_str}&apikey={api_key}" - return get_data_many(url, FMPStockEODData, "historical") + + return get_data_many(url, FMPStockEODData, "historical", **kwargs) @staticmethod - def transform_data(data: List[FMPStockEODData]) -> List[StockEODData]: - return data_transformer(data, StockEODData) + def transform_data(data: List[FMPStockEODData]) -> List[FMPStockEODData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_insider_trading.py b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_insider_trading.py index 2411f29c6e2..d1d4d157f5b 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_insider_trading.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_insider_trading.py @@ -1,98 +1,81 @@ """FMP Stock Insider Trading fetcher.""" -from datetime import date, datetime -from typing import Dict, List, Optional +from datetime import datetime +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.stock_insider_trading import ( +from openbb_provider.standard_models.stock_insider_trading import ( StockInsiderTradingData, StockInsiderTradingQueryParams, ) -from pydantic import Field +from pydantic import validator from openbb_fmp.utils.helpers import create_url, get_data_many class FMPStockInsiderTradingQueryParams(StockInsiderTradingQueryParams): - """FMP Stock Insider Trading query. + """FMP Stock Insider Trading Query. Source: https://site.financialmodelingprep.com/developer/docs/#Stock-Insider-Trading - - Parameter - --------- - transactionType : List[TransactionTypes] - The type of the transaction. Possible values are: - A-Award, C-Conversion, D-Return, E-ExpireShort, F-InKind, G-Gift, H-ExpireLong - I-Discretionary, J-Other, L-Small, M-Exempt, O-OutOfTheMoneym P-Purchase - S-Sale, U-Tender, W-Will, X-InTheMoney, Z-Trust - symbol : str] - The symbol of the company. - reportingCik : str] - The CIK of the reporting owner. - companyCik: str] - The CIK of the company owner. - page: int - The page number to get """ -class FMPStockInsiderTradingData(Data): - """FMP Stock Insider Trading data.""" +class FMPStockInsiderTradingData(StockInsiderTradingData): + """FMP Stock Insider Trading Data.""" - symbol: str - filingDate: datetime = Field(alias="filing_date") - transactionDate: date = Field(alias="transaction_date") - reportingCik: int = Field(alias="reporting_cik") - transactionType: str = Field(alias="transaction_type") - securitiesOwned: int = Field(alias="securities_owned") - companyCik: int = Field(alias="company_cik") - reportingName: str = Field(alias="reporting_name") - typeOfOwner: str = Field(alias="type_of_owner") - acquistionOrDisposition: str = Field(alias="acquistion_or_disposition") - formType: str = Field(alias="form_type") - securitiesTransacted: float = Field(alias="securities_transacted") - price: float - securityName: str = Field(alias="security_name") - link: str + class Config: + fields = { + "filing_date": "filingDate", + "transaction_date": "transactionDate", + "reporting_cik": "reportingCik", + "transaction_type": "transactionType", + "securities_owned": "securitiesOwned", + "company_cik": "companyCik", + "reporting_name": "reportingName", + "type_of_owner": "typeOfOwner", + "acquistion_or_disposition": "acquistionOrDisposition", + "form_type": "formType", + "securities_transacted": "securitiesTransacted", + "security_name": "securityName", + } + + @validator("filingDate", pre=True, check_fields=False) + def filing_date_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d %H:%M:%S") + + @validator("transactionDate", pre=True, check_fields=False) + def transaction_date_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d").date() class FMPStockInsiderTradingFetcher( Fetcher[ - StockInsiderTradingQueryParams, - StockInsiderTradingData, FMPStockInsiderTradingQueryParams, FMPStockInsiderTradingData, ] ): @staticmethod - def transform_query( - query: StockInsiderTradingQueryParams, extra_params: Optional[Dict] = None - ) -> FMPStockInsiderTradingQueryParams: - return FMPStockInsiderTradingQueryParams( - transactionType=query.transactionType, - symbol=query.symbol, - reportingCik=query.reportingCik, - companyCik=query.companyCik, - page=query.page, - ) + def transform_query(params: Dict[str, Any]) -> FMPStockInsiderTradingQueryParams: + return FMPStockInsiderTradingQueryParams(**params) @staticmethod def extract_data( - query: FMPStockInsiderTradingQueryParams, credentials: Optional[Dict[str, str]] + query: FMPStockInsiderTradingQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any ) -> List[FMPStockInsiderTradingData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" # This changes the actual type of a pydantic class, but its a quick and clean way to format properly query.transactionType = ",".join(query.transactionType) # type: ignore url = create_url(4, "insider-trading", api_key, query) - return get_data_many(url, FMPStockInsiderTradingData) + + return get_data_many(url, FMPStockInsiderTradingData, **kwargs) @staticmethod def transform_data( data: List[FMPStockInsiderTradingData], - ) -> List[StockInsiderTradingData]: - return data_transformer(data, StockInsiderTradingData) + ) -> List[FMPStockInsiderTradingData]: + return data + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_multiples.py b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_multiples.py index a58a0cf0d26..909ba064ce2 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_multiples.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_multiples.py @@ -1,157 +1,115 @@ """FMP Stock Multiples Fetcher.""" -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.stock_multiples import ( +from openbb_provider.standard_models.stock_multiples import ( StockMultiplesData, StockMultiplesQueryParams, ) -from pydantic import Field -from openbb_fmp.utils.helpers import create_url, get_data_one +from openbb_fmp.utils.helpers import create_url, get_data_many class FMPStockMultiplesQueryParams(StockMultiplesQueryParams): - """FMP Stock Multiples QueryParams. + """FMP Stock Multiples Query. Source: https://site.financialmodelingprep.com/developer/docs/#Company-Key-Metrics - - Parameter - --------- - symbol : str - The symbol of the company. - limit : Optional[int] - The limit of the key metrics ttm to be returned. """ class FMPStockMultiplesData(StockMultiplesData): """FMP Stock Multiples Data.""" - revenuePerShareTTM: Optional[float] = Field(alias="revenue_per_share_ttm") - netIncomePerShareTTM: Optional[float] = Field(alias="net_income_per_share_ttm") - operatingCashFlowPerShareTTM: Optional[float] = Field( - alias="operating_cash_flow_per_share_ttm" - ) - freeCashFlowPerShareTTM: Optional[float] = Field( - alias="free_cash_flow_per_share_ttm" - ) - cashPerShareTTM: Optional[float] = Field(alias="cash_per_share_ttm") - bookValuePerShareTTM: Optional[float] = Field(alias="book_value_per_share_ttm") - tangibleBookValuePerShareTTM: Optional[float] = Field( - alias="tangible_book_value_per_share_ttm" - ) - shareholdersEquityPerShareTTM: Optional[float] = Field( - alias="shareholders_equity_per_share_ttm" - ) - interestDebtPerShareTTM: Optional[float] = Field( - alias="interest_debt_per_share_ttm" - ) - marketCapTTM: Optional[float] = Field(alias="market_cap_ttm") - enterpriseValueTTM: Optional[float] = Field(alias="enterprise_value_ttm") - peRatioTTM: Optional[float] = Field(alias="pe_ratio_ttm") - priceToSalesRatioTTM: Optional[float] = Field(alias="price_to_sales_ratio_ttm") - pocfratioTTM: Optional[float] = Field(alias="pocf_ratio_ttm") - pfcfRatioTTM: Optional[float] = Field(alias="pfcf_ratio_ttm") - pbRatioTTM: Optional[float] = Field(alias="pb_ratio_ttm") - ptbRatioTTM: Optional[float] = Field(alias="ptb_ratio_ttm") - evToSalesTTM: Optional[float] = Field(alias="ev_to_sales_ttm") - enterpriseValueOverEBITDATTM: Optional[float] = Field( - alias="enterprise_value_over_ebitda_ttm" - ) - evToOperatingCashFlowTTM: Optional[float] = Field( - alias="ev_to_operating_cash_flow_ttm" - ) - evToFreeCashFlowTTM: Optional[float] = Field(alias="ev_to_free_cash_flow_ttm") - earningsYieldTTM: Optional[float] = Field(alias="earnings_yield_ttm") - freeCashFlowYieldTTM: Optional[Optional[float]] = Field( - alias="free_cash_flow_yield_ttm" - ) - debtToEquityTTM: Optional[float] = Field(alias="debt_to_equity_ttm") - debtToAssetsTTM: Optional[float] = Field(alias="debt_to_assets_ttm") - netDebtToEBITDATTM: Optional[float] = Field(alias="net_debt_to_ebitda_ttm") - currentRatioTTM: Optional[float] = Field(alias="current_ratio_ttm") - interestCoverageTTM: Optional[float] = Field(alias="interest_coverage_ttm") - incomeQualityTTM: Optional[float] = Field(alias="income_quality_ttm") - dividendYieldTTM: Optional[Optional[float]] = Field(alias="dividend_yield_ttm") - payoutRatioTTM: Optional[Optional[float]] = Field(alias="payout_ratio_ttm") - salesGeneralAndAdministrativeToRevenueTTM: Optional[float] = Field( - alias="sales_general_and_administrative_to_revenue_ttm" - ) - researchAndDevelopementToRevenueTTM: Optional[float] = Field( - alias="research_and_development_to_revenue_ttm" - ) - intangiblesToTotalAssetsTTM: Optional[float] = Field( - alias="intangibles_to_total_assets_ttm" - ) - capexToOperatingCashFlowTTM: Optional[float] = Field( - alias="capex_to_operating_cash_flow_ttm" - ) - capexToRevenueTTM: Optional[float] = Field(alias="capex_to_revenue_ttm") - capexToDepreciationTTM: Optional[float] = Field(alias="capex_to_depreciation_ttm") - stockBasedCompensationToRevenueTTM: Optional[float] = Field( - alias="stock_based_compensation_to_revenue_ttm" - ) - grahamNumberTTM: Optional[float] = Field(alias="graham_number_ttm") - roicTTM: Optional[float] = Field(alias="roic_ttm") - returnOnTangibleAssetsTTM: Optional[float] = Field( - alias="return_on_tangible_assets_ttm" - ) - grahamNetNetTTM: Optional[float] = Field(alias="graham_net_net_ttm") - workingCapitalTTM: Optional[float] = Field(alias="working_capital_ttm") - tangibleAssetValueTTM: Optional[float] = Field(alias="tangible_asset_value_ttm") - netCurrentAssetValueTTM: Optional[float] = Field( - alias="net_current_asset_value_ttm" - ) - investedCapitalTTM: Optional[float] = Field(alias="invested_capital_ttm") - averageReceivablesTTM: Optional[float] = Field(alias="average_receivables_ttm") - averagePayablesTTM: Optional[float] = Field(alias="average_payables_ttm") - averageInventoryTTM: Optional[float] = Field(alias="average_inventory_ttm") - daysSalesOutstandingTTM: Optional[float] = Field(alias="days_sales_outstanding_ttm") - daysPayablesOutstandingTTM: Optional[float] = Field( - alias="days_payables_outstanding_ttm" - ) - daysOfInventoryOnHandTTM: Optional[float] = Field( - alias="days_of_inventory_on_hand_ttm" - ) - receivablesTurnoverTTM: Optional[float] = Field(alias="receivables_turnover_ttm") - payablesTurnoverTTM: Optional[float] = Field(alias="payables_turnover_ttm") - inventoryTurnoverTTM: Optional[float] = Field(alias="inventory_turnover_ttm") - roeTTM: Optional[float] = Field(alias="roe_ttm") - capexPerShareTTM: Optional[float] = Field(alias="capex_per_share_ttm") + class Config: + fields = { + "revenue_per_share_ttm": "revenuePerShareTTM", + "net_income_per_share_ttm": "netIncomePerShareTTM", + "operating_cash_flow_per_share_ttm": "operatingCashFlowPerShareTTM", + "free_cash_flow_per_share_ttm": "freeCashFlowPerShareTTM", + "cash_per_share_ttm": "cashPerShareTTM", + "book_value_per_share_ttm": "bookValuePerShareTTM", + "tangible_book_value_per_share_ttm": "tangibleBookValuePerShareTTM", + "shareholders_equity_per_share_ttm": "shareholdersEquityPerShareTTM", + "interest_debt_per_share_ttm": "interestDebtPerShareTTM", + "market_cap_ttm": "marketCapTTM", + "enterprise_value_ttm": "enterpriseValueTTM", + "pe_ratio_ttm": "peRatioTTM", + "price_to_sales_ratio_ttm": "priceToSalesRatioTTM", + "pocf_ratio_ttm": "pocfratioTTM", + "pfcf_ratio_ttm": "pfcfRatioTTM", + "pb_ratio_ttm": "pbRatioTTM", + "ptb_ratio_ttm": "ptbRatioTTM", + "ev_to_sales_ttm": "evToSalesTTM", + "enterprise_value_over_ebitda_ttm": "enterpriseValueOverEBITDATTM", + "ev_to_operating_cash_flow_ttm": "evToOperatingCashFlowTTM", + "ev_to_free_cash_flow_ttm": "evToFreeCashFlowTTM", + "earnings_yield_ttm": "earningsYieldTTM", + "free_cash_flow_yield_ttm": "freeCashFlowYieldTTM", + "debt_to_equity_ttm": "debtToEquityTTM", + "debt_to_assets_ttm": "debtToAssetsTTM", + "net_debt_to_ebitda_ttm": "netDebtToEBITDATTM", + "current_ratio_ttm": "currentRatioTTM", + "interest_coverage_ttm": "interestCoverageTTM", + "income_quality_ttm": "incomeQualityTTM", + "dividend_yield_ttm": "dividendYieldTTM", + "payout_ratio_ttm": "payoutRatioTTM", + "sales_general_and_administrative_to_revenue_ttm": "salesGeneralAndAdministrativeToRevenueTTM", + "research_and_development_to_revenue_ttm": "researchAndDevelopementToRevenueTTM", + "intangibles_to_total_assets_ttm": "intangiblesToTotalAssetsTTM", + "capex_to_operating_cash_flow_ttm": "capexToOperatingCashFlowTTM", + "capex_to_revenue_ttm": "capexToRevenueTTM", + "capex_to_depreciation_ttm": "capexToDepreciationTTM", + "stock_based_compensation_to_revenue_ttm": "stockBasedCompensationToRevenueTTM", + "graham_number_ttm": "grahamNumberTTM", + "roic_ttm": "roicTTM", + "return_on_tangible_assets_ttm": "returnOnTangibleAssetsTTM", + "graham_net_net_ttm": "grahamNetNetTTM", + "working_capital_ttm": "workingCapitalTTM", + "tangible_asset_value_ttm": "tangibleAssetValueTTM", + "net_current_asset_value_ttm": "netCurrentAssetValueTTM", + "invested_capital_ttm": "investedCapitalTTM", + "average_receivables_ttm": "averageReceivablesTTM", + "average_payables_ttm": "averagePayablesTTM", + "average_inventory_ttm": "averageInventoryTTM", + "days_sales_outstanding_ttm": "daysSalesOutstandingTTM", + "days_payables_outstanding_ttm": "daysPayablesOutstandingTTM", + "days_of_inventory_on_hand_ttm": "daysOfInventoryOnHandTTM", + "receivables_turnover_ttm": "receivablesTurnoverTTM", + "payables_turnover_ttm": "payablesTurnoverTTM", + "inventory_turnover_ttm": "inventoryTurnoverTTM", + "roe_ttm": "roeTTM", + "capex_per_share_ttm": "capexPerShareTTM", + } class FMPStockMultiplesFetcher( Fetcher[ - StockMultiplesQueryParams, - StockMultiplesData, FMPStockMultiplesQueryParams, FMPStockMultiplesData, ] ): @staticmethod - def transform_query( - query: StockMultiplesQueryParams, extra_params: Optional[Dict] = None - ) -> FMPStockMultiplesQueryParams: - return FMPStockMultiplesQueryParams( - symbol=query.symbol, limit=query.limit, **extra_params or {} - ) + def transform_query(params: Dict[str, Any]) -> FMPStockMultiplesQueryParams: + return FMPStockMultiplesQueryParams(**params) @staticmethod def extract_data( - query: FMPStockMultiplesQueryParams, credentials: Optional[Dict[str, str]] + query: FMPStockMultiplesQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPStockMultiplesData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url( 3, f"key-metrics-ttm/{query.symbol}", api_key, query, exclude=["symbol"] ) - return get_data_one(url, FMPStockMultiplesData) + + return get_data_many(url, FMPStockMultiplesData, **kwargs) @staticmethod - def transform_data(data: List[FMPStockMultiplesData]) -> List[StockMultiplesData]: - return data_transformer(data, StockMultiplesData) + def transform_data( + data: List[FMPStockMultiplesData], + ) -> List[FMPStockMultiplesData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_news.py b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_news.py index bc9a5b337d4..90f9a1ded37 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_news.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_news.py @@ -1,76 +1,61 @@ """FMP Stock News fetcher.""" -from datetime import datetime -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data, QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.stock_news import StockNewsData, StockNewsQueryParams -from pydantic import Field, NonNegativeInt +from openbb_provider.standard_models.stock_news import ( + StockNewsData, + StockNewsQueryParams, +) +from pydantic import Field from openbb_fmp.utils.helpers import create_url, get_data_many -class FMPStockNewsQueryParams(QueryParams): +class FMPStockNewsQueryParams(StockNewsQueryParams): """FMP Stock News query. Source: https://site.financialmodelingprep.com/developer/docs/stock-news-api/ - - Parameter - --------- - symbols : str - The symbols of the companies. - page : int (default: 0) - The page of the data to retrieve. - limit : Optional[int] - The limit of the data to retrieve. """ - tickers: str = Field(min_length=1, alias="symbols") - page: int = Field(default=0) - limit: Optional[NonNegativeInt] + class Config: + fields = {"symbols": "tickers"} -class FMPStockNewsData(Data): - symbol: str - publishedDate: datetime = Field(alias="date") - title: str - image: Optional[str] - text: str - url: str - site: str +class FMPStockNewsData(StockNewsData): + """FMP Stock News data.""" + + class Config: + fields = {"date": "publishedDate"} + + symbol: str = Field(description="Ticker of the fetched news.") + image: Optional[str] = Field(description="URL to the image of the news source.") + site: str = Field(description="Name of the news source.") class FMPStockNewsFetcher( Fetcher[ - StockNewsQueryParams, - StockNewsData, FMPStockNewsQueryParams, FMPStockNewsData, ] ): @staticmethod - def transform_query( - query: StockNewsQueryParams, extra_params: Optional[Dict] = None - ) -> FMPStockNewsQueryParams: - return FMPStockNewsQueryParams( - symbols=query.symbols, - page=query.page, - **extra_params if extra_params else {} - ) + def transform_query(params: Dict[str, Any]) -> FMPStockNewsQueryParams: + return FMPStockNewsQueryParams(**params) @staticmethod def extract_data( - query: FMPStockNewsQueryParams, credentials: Optional[Dict[str, str]] + query: FMPStockNewsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any ) -> List[FMPStockNewsData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url(3, "stock_news", api_key, query) - return get_data_many(url, FMPStockNewsData) + return get_data_many(url, FMPStockNewsData, **kwargs) @staticmethod - def transform_data(data: List[FMPStockNewsData]) -> List[StockNewsData]: - return data_transformer(data, StockNewsData) + def transform_data(data: List[FMPStockNewsData]) -> List[FMPStockNewsData]: + return data + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_ownership.py b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_ownership.py index 995e046021e..b7d97f5ab9d 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_ownership.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_ownership.py @@ -1,11 +1,10 @@ """FMP Stock Ownership fetcher.""" -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.stock_ownership import ( +from openbb_provider.standard_models.stock_ownership import ( StockOwnershipData, StockOwnershipQueryParams, ) @@ -17,42 +16,67 @@ class FMPStockOwnershipQueryParams(StockOwnershipQueryParams): """FMP Stock Ownership query. Source: https://site.financialmodelingprep.com/developer/docs/#Stock-Ownership-by-Holders - - Parameter - --------- - symbol : string - The symbol of the company. - page : int - The page number to get - date : date - The CIK of the company owner. """ class FMPStockOwnershipData(StockOwnershipData): - """FMP Stock Ownership data.""" + """FMP Stock Ownership Data.""" + + class Config: + fields = { + "filing_date": "filingDate", + "investor_name": "investorName", + "security_name": "securityName", + "type_of_security": "typeOfSecurity", + "security_cusip": "securityCusip", + "shares_type": "sharesType", + "put_call_share": "putCallShare", + "investment_discretion": "investmentDiscretion", + "industry_title": "industryTitle", + "last_weight": "lastWeight", + "change_in_weight": "changeInWeight", + "change_in_weight_percentage": "changeInWeightPercentage", + "market_value": "marketValue", + "last_market_value": "lastMarketValue", + "change_in_market_value": "changeInMarketValue", + "change_in_market_value_percentage": "changeInMarketValuePercentage", + "shares_number": "sharesNumber", + "last_shares_number": "lastSharesNumber", + "change_in_shares_number": "changeInSharesNumber", + "change_in_shares_number_percentage": "changeInSharesNumberPercentage", + "quarter_end_price": "quarterEndPrice", + "avg_price_paid": "avgPricePaid", + "is_new": "isNew", + "is_sold_out": "isSoldOut", + "last_ownership": "lastOwnership", + "change_in_ownership": "changeInOwnership", + "change_in_ownership_percentage": "changeInOwnershipPercentage", + "holding_period": "holdingPeriod", + "first_added": "firstAdded", + "performance_percentage": "performancePercentage", + "last_performance": "lastPerformance", + "change_in_performance": "changeInPerformance", + "is_counted_for_performance": "isCountedForPerformance", + } class FMPStockOwnershipFetcher( Fetcher[ - StockOwnershipQueryParams, - StockOwnershipData, FMPStockOwnershipQueryParams, FMPStockOwnershipData, ] ): @staticmethod - def transform_query( - query: StockOwnershipQueryParams, extra_params: Optional[Dict] = None - ) -> FMPStockOwnershipQueryParams: - return FMPStockOwnershipQueryParams.parse_obj(query) + def transform_query(params: Dict[str, Any]) -> FMPStockOwnershipQueryParams: + return FMPStockOwnershipQueryParams(**params) @staticmethod def extract_data( - query: FMPStockOwnershipQueryParams, credentials: Optional[Dict[str, str]] + query: FMPStockOwnershipQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any ) -> List[FMPStockOwnershipData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url( 4, @@ -60,8 +84,11 @@ class FMPStockOwnershipFetcher( api_key, query, ) - return get_data_many(url, FMPStockOwnershipData) + + return get_data_many(url, FMPStockOwnershipData, **kwargs) @staticmethod - def transform_data(data: List[FMPStockOwnershipData]) -> List[StockOwnershipData]: - return data_transformer(data, StockOwnershipData) + def transform_data( + data: List[FMPStockOwnershipData], + ) -> List[FMPStockOwnershipData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_peers.py b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_peers.py index 4f0594da452..69c2f9d18a3 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_peers.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_peers.py @@ -1,11 +1,13 @@ """FMP Stock Peers fetcher.""" -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.stock_peers import StockPeersData, StockPeersQueryParams +from openbb_provider.standard_models.stock_peers import ( + StockPeersData, + StockPeersQueryParams, +) from openbb_fmp.utils.helpers import create_url, get_data_many @@ -16,42 +18,39 @@ class FMPStockPeersQueryParams(StockPeersQueryParams): """FMP Stock Peers query. Source: https://site.financialmodelingprep.com/developer/docs/#Stock-Peers - - Parameter - --------- - symbol : str - The symbol of the company. """ class FMPStockPeersData(StockPeersData): """FMP Stock Peers data.""" + class Config: + fields = { + "peers_list": "peersList", + } + class FMPStockPeersFetcher( Fetcher[ - StockPeersQueryParams, - StockPeersData, FMPStockPeersQueryParams, FMPStockPeersData, ] ): @staticmethod - def transform_query( - query: StockPeersQueryParams, extra_params: Optional[Dict] = None - ) -> FMPStockPeersQueryParams: - return FMPStockPeersQueryParams(symbol=query.symbol, **extra_params or {}) + def transform_query(params: Dict[str, Any]) -> FMPStockPeersQueryParams: + return FMPStockPeersQueryParams(**params) @staticmethod def extract_data( - query: FMPStockPeersQueryParams, credentials: Optional[Dict[str, str]] + query: FMPStockPeersQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any ) -> List[FMPStockPeersData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" url = create_url(4, "stock_peers", api_key, query) - return get_data_many(url, FMPStockPeersData) + return get_data_many(url, FMPStockPeersData, **kwargs) @staticmethod - def transform_data(data: List[FMPStockPeersData]) -> List[StockPeersData]: - return data_transformer(data, StockPeersData) + def transform_data(data: List[FMPStockPeersData]) -> List[FMPStockPeersData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_price.py b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_price.py deleted file mode 100644 index e4905760a38..00000000000 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_price.py +++ /dev/null @@ -1,76 +0,0 @@ -"""FMP Stock Price fetcher.""" - - -from enum import Enum -from typing import Dict, List, Literal, Optional - -from openbb_provider.abstract.data import QueryParams -from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.stock_price import StockPriceData, StockPriceQueryParams -from pydantic import Field - -from openbb_fmp.utils.helpers import BaseStockPriceData, create_url, get_data_many - - -class Interval(str, Enum): - oneminute = "1min" - fiveminute = "5min" - fifteenminute = "15min" - thirtyminute = "30min" - onehour = "1hour" - fourhour = "4hour" - - -class FMPStockPriceQueryParams(QueryParams): - """FMP Stock Price query. - - Source: https://site.financialmodelingprep.com/developer/docs/#Stock-Historical-Price - - Parameter - --------- - symbol : str - The symbol of the company. - interval : Enum of ["1min", "5min", "15min", "30min", "1hour", "4hour"] (default: "1hour") - The interval of the data. - """ - - symbol: str = Field(min_length=1) - interval: Literal["1min", "5min", "15min", "30min", "1hour", "4hour"] = "1hour" - - -class FMPStockPriceData(BaseStockPriceData): - """FMP Stock Price data.""" - - -class FMPStockPriceFetcher( - Fetcher[ - StockPriceQueryParams, - StockPriceData, - FMPStockPriceQueryParams, - FMPStockPriceData, - ] -): - @staticmethod - def transform_query( - query: StockPriceQueryParams, extra_params: Optional[Dict] = None - ) -> FMPStockPriceQueryParams: - return FMPStockPriceQueryParams( - symbol=query.symbol, **extra_params if extra_params else {} - ) - - @staticmethod - def extract_data( - query: FMPStockPriceQueryParams, credentials: Optional[Dict[str, str]] - ) -> List[FMPStockPriceData]: - if credentials: - api_key = credentials.get("fmp_api_key") - - url = create_url( - 3, f"historical-chart/{query.interval}/{query.symbol}", api_key - ) - return get_data_many(url, FMPStockPriceData) - - @staticmethod - def transform_data(data: List[FMPStockPriceData]) -> List[StockPriceData]: - return data_transformer(data, StockPriceData) diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_splits.py b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_splits.py index ec9083af433..5bf66f429cb 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_splits.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_splits.py @@ -2,11 +2,10 @@ from datetime import date -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.stock_splits import ( +from openbb_provider.standard_models.stock_splits import ( StockSplitCalendarData, StockSplitCalendarQueryParams, ) @@ -18,18 +17,8 @@ class FMPStockSplitCalendarQueryParams(StockSplitCalendarQueryParams): """FMP Stock Split Calendar query. Source: https://site.financialmodelingprep.com/developer/docs/stock-split-calendar-api/ - - Parameter - --------- - start_date : date - The start date of the stock splits from which to retrieve the data. - end_date : date - The end date of the stock splits up to which to retrieve the data. """ - start_date: date - end_date: date - class FMPStockSplitCalendarData(StockSplitCalendarData): """FMP Stock Split Calendar data.""" @@ -37,37 +26,36 @@ class FMPStockSplitCalendarData(StockSplitCalendarData): class FMPStockSplitCalendarFetcher( Fetcher[ - StockSplitCalendarQueryParams, - StockSplitCalendarData, FMPStockSplitCalendarQueryParams, FMPStockSplitCalendarData, ] ): @staticmethod - def transform_query( - query: StockSplitCalendarQueryParams, extra_params: Optional[Dict] = None - ) -> FMPStockSplitCalendarQueryParams: - now = date.today() - start_date = query.start_date if query.start_date else now - end_date = query.end_date if query.end_date else now - return FMPStockSplitCalendarQueryParams( - start_date=start_date, - end_date=end_date, - ) + def transform_query(params: Dict[str, Any]) -> FMPStockSplitCalendarQueryParams: + today = date.today() + transformed_params = params + if params.get("start_date") is None: + transformed_params["start_date"] = today + + if params.get("end_date") is None: + transformed_params["end_date"] = today + + return FMPStockSplitCalendarQueryParams(**transformed_params) @staticmethod def extract_data( - query: FMPStockSplitCalendarQueryParams, credentials: Optional[Dict[str, str]] + query: FMPStockSplitCalendarQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPStockSplitCalendarData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" query_str = f"from={query.start_date}&to={query.end_date}" url = create_url(3, f"stock_split_calendar?{query_str}", api_key) - return get_data_many(url, FMPStockSplitCalendarData) + return get_data_many(url, FMPStockSplitCalendarData, **kwargs) @staticmethod def transform_data( data: List[FMPStockSplitCalendarData], - ) -> List[StockSplitCalendarData]: - return data_transformer(data, StockSplitCalendarData) + ) -> List[FMPStockSplitCalendarData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/treasury_rates.py b/openbb_sdk/providers/fmp/openbb_fmp/models/treasury_rates.py index a7523e92436..a5724e372fd 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/treasury_rates.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/treasury_rates.py @@ -1,80 +1,81 @@ """FMP Treasury Rates fetcher.""" -from datetime import date as dateType -from typing import Dict, List, Optional +from datetime import datetime, timedelta +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer, get_querystring -from openbb_provider.models.treasury_rates import ( +from openbb_provider.standard_models.treasury_rates import ( TreasuryRatesData, TreasuryRatesQueryParams, ) -from pydantic import Field +from pydantic import validator -from openbb_fmp.utils.helpers import get_data_many +from openbb_fmp.utils.helpers import get_data_many, get_querystring class FMPTreasuryRatesQueryParams(TreasuryRatesQueryParams): - """FMP Stock News query. + """FMP Stock News Query. Source: https://site.financialmodelingprep.com/developer/docs/treasury-rates-api/ - Parameter - --------- - start_date : str - The start date of the data. - end_date : str - The end date of the data. Default is today. + Maximum time interval can be 3 months. """ -class FMPTreasuryRatesData(Data): - date: dateType - month1: float = Field(alias="month_1") - month2: float = Field(alias="month_2") - month3: float = Field(alias="month_3") - month6: float = Field(alias="month_6") - year1: float = Field(alias="year_1") - year2: float = Field(alias="year_2") - year3: float = Field(alias="year_3") - year5: float = Field(alias="year_5") - year7: float = Field(alias="year_7") - year10: float = Field(alias="year_10") - year20: float = Field(alias="year_20") - year30: float = Field(alias="year_30") +class FMPTreasuryRatesData(TreasuryRatesData): + """FMP Treasury Rates Data.""" + + class Config: + fields = { + "month_1": "month1", + "month_2": "month2", + "month_3": "month3", + "month_6": "month6", + "year_1": "year1", + "year_2": "year2", + "year_3": "year3", + "year_5": "year5", + "year_7": "year7", + "year_10": "year10", + "year_20": "year20", + "year_30": "year30", + } + + @validator("date", pre=True, check_fields=False) + def date_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d") class FMPTreasuryRatesFetcher( Fetcher[ - TreasuryRatesQueryParams, - TreasuryRatesData, FMPTreasuryRatesQueryParams, FMPTreasuryRatesData, ] ): @staticmethod - def transform_query( - query: TreasuryRatesQueryParams, extra_params: Optional[Dict] = None - ) -> FMPTreasuryRatesQueryParams: - return FMPTreasuryRatesQueryParams( - start_date=query.start_date, end_date=query.end_date - ) + def transform_query(params: Dict[str, Any]) -> FMPTreasuryRatesQueryParams: + return FMPTreasuryRatesQueryParams(**params) @staticmethod def extract_data( - query: FMPTreasuryRatesQueryParams, credentials: Optional[Dict[str, str]] + query: FMPTreasuryRatesQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[FMPTreasuryRatesData]: - if credentials: - api_key = credentials.get("fmp_api_key") + api_key = credentials.get("fmp_api_key") if credentials else "" + + query.start_date = (datetime.now() - timedelta(91)).date() + query.end_date = (datetime.now() - timedelta(1)).date() base_url = "https://financialmodelingprep.com/api/v4/" + # query_str = get_querystring(query.dict(by_alias=True), []) query_str = get_querystring(query.dict(), []) query_str = query_str.replace("start_date", "from").replace("end_date", "to") url = f"{base_url}treasury?{query_str}&apikey={api_key}" - return get_data_many(url, FMPTreasuryRatesData) + + return get_data_many(url, FMPTreasuryRatesData, **kwargs) @staticmethod - def transform_data(data: List[FMPTreasuryRatesData]) -> List[TreasuryRatesData]: - return data_transformer(data, TreasuryRatesData) + def transform_data(data: List[FMPTreasuryRatesData]) -> List[FMPTreasuryRatesData]: + return data diff --git a/openbb_sdk/providers/fmp/openbb_fmp/provider.py b/openbb_sdk/providers/fmp/openbb_fmp/provider.py deleted file mode 100644 index 156ceae8325..00000000000 --- a/openbb_sdk/providers/fmp/openbb_fmp/provider.py +++ /dev/null @@ -1,105 +0,0 @@ -"""FMP Provider module.""" - - -from openbb_provider.abstract.provider import Provider - -from openbb_fmp.models.analyst_estimates import FMPAnalystEstimatesFetcher -from openbb_fmp.models.available_indices import FMPAvailableIndicesFetcher -from openbb_fmp.models.balance_sheet import FMPBalanceSheetFetcher -from openbb_fmp.models.cash_flow import FMPCashFlowStatementFetcher -from openbb_fmp.models.company_profile import FMPCompanyProfileFetcher -from openbb_fmp.models.crypto_eod import FMPCryptoEODFetcher -from openbb_fmp.models.crypto_price import FMPCryptoPriceFetcher -from openbb_fmp.models.dividend_calendar import FMPDividendCalendarFetcher -from openbb_fmp.models.earnings_calendar import FMPEarningsCalendarFetcher -from openbb_fmp.models.earnings_call_transcript import FMPEarningsCallTranscriptFetcher -from openbb_fmp.models.esg_risk_rating import FMPESGRiskRatingFetcher -from openbb_fmp.models.esg_score import FMPESGScoreFetcher -from openbb_fmp.models.esg_sector import FMPESGSectorFetcher -from openbb_fmp.models.executive_compensation import FMPExecutiveCompensationFetcher -from openbb_fmp.models.executives import FMPKeyExecutivesFetcher -from openbb_fmp.models.forex_eod import FMPForexEODFetcher -from openbb_fmp.models.forex_pairs import FMPForexPairsFetcher -from openbb_fmp.models.forex_price import FMPForexPriceFetcher -from openbb_fmp.models.global_news import FMPGlobalNewsFetcher -from openbb_fmp.models.historical_dividends import FMPHistoricalDividendsFetcher -from openbb_fmp.models.historical_employees import FMPHistoricalEmployeesFetcher -from openbb_fmp.models.historical_stock_splits import FMPHistoricalStockSplitsFetcher -from openbb_fmp.models.income_statement import FMPIncomeStatementFetcher -from openbb_fmp.models.institutional_ownership import FMPInstitutionalOwnershipFetcher -from openbb_fmp.models.key_metrics import FMPKeyMetricsFetcher -from openbb_fmp.models.major_indices_constituents import ( - FMPMajorIndicesConstituentsFetcher, -) -from openbb_fmp.models.major_indices_eod import FMPMajorIndicesEODFetcher -from openbb_fmp.models.major_indices_price import FMPMajorIndicesPriceFetcher -from openbb_fmp.models.price_target import FMPPriceTargetFetcher -from openbb_fmp.models.price_target_consensus import FMPPriceTargetConsensusFetcher -from openbb_fmp.models.revenue_business_line import FMPRevenueBusinessLineFetcher -from openbb_fmp.models.revenue_geographic import FMPRevenueGeographicFetcher -from openbb_fmp.models.risk_premium import FMPRiskPremiumFetcher -from openbb_fmp.models.sec_filings import FMPSECFilingsFetcher -from openbb_fmp.models.share_statistics import FMPShareStatisticsFetcher -from openbb_fmp.models.stock_eod import FMPStockEODFetcher -from openbb_fmp.models.stock_insider_trading import FMPStockInsiderTradingFetcher -from openbb_fmp.models.stock_multiples import FMPStockMultiplesFetcher -from openbb_fmp.models.stock_news import FMPStockNewsFetcher -from openbb_fmp.models.stock_ownership import FMPStockOwnershipFetcher -from openbb_fmp.models.stock_peers import FMPStockPeersFetcher -from openbb_fmp.models.stock_price import FMPStockPriceFetcher -from openbb_fmp.models.stock_splits import FMPStockSplitCalendarFetcher -from openbb_fmp.models.treasury_rates import FMPTreasuryRatesFetcher - -# mypy: disable-error-code="list-item" - -fmp_provider = Provider( - name="fmp", - description="Provider for FMP.", - required_credentials=["api_key"], - fetcher_list=[ - FMPKeyExecutivesFetcher, - FMPStockEODFetcher, - FMPGlobalNewsFetcher, - FMPStockNewsFetcher, - FMPIncomeStatementFetcher, - FMPBalanceSheetFetcher, - FMPCashFlowStatementFetcher, - FMPShareStatisticsFetcher, - FMPMajorIndicesEODFetcher, - FMPRevenueGeographicFetcher, - FMPRevenueBusinessLineFetcher, - FMPInstitutionalOwnershipFetcher, - FMPCompanyProfileFetcher, - FMPStockInsiderTradingFetcher, - FMPStockOwnershipFetcher, - FMPESGScoreFetcher, - FMPESGSectorFetcher, - FMPESGRiskRatingFetcher, - FMPStockPriceFetcher, - FMPPriceTargetConsensusFetcher, - FMPPriceTargetFetcher, - FMPAnalystEstimatesFetcher, - FMPEarningsCalendarFetcher, - FMPEarningsCallTranscriptFetcher, - FMPHistoricalStockSplitsFetcher, - FMPStockSplitCalendarFetcher, - FMPHistoricalDividendsFetcher, - FMPKeyMetricsFetcher, - FMPSECFilingsFetcher, - FMPTreasuryRatesFetcher, - FMPExecutiveCompensationFetcher, - FMPCryptoPriceFetcher, - FMPCryptoEODFetcher, - FMPMajorIndicesPriceFetcher, - FMPForexEODFetcher, - FMPForexPriceFetcher, - FMPForexPairsFetcher, - FMPStockPeersFetcher, - FMPStockMultiplesFetcher, - FMPHistoricalEmployeesFetcher, - FMPAvailableIndicesFetcher, - FMPRiskPremiumFetcher, - FMPMajorIndicesConstituentsFetcher, - FMPDividendCalendarFetcher, - ], -) diff --git a/openbb_sdk/providers/fmp/openbb_fmp/utils/helpers.py b/openbb_sdk/providers/fmp/openbb_fmp/utils/helpers.py index 4de0aa4f73b..875954134e2 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/utils/helpers.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/utils/helpers.py @@ -1,16 +1,16 @@ """FMP Helpers Module.""" - +import json from datetime import datetime -from typing import List, Optional, Type, TypeVar, Union +from io import StringIO +from typing import Any, List, Optional, Type, TypeVar, Union import requests +from openbb_provider import helpers from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import QueryParamsType -from openbb_provider.helpers import ( - BasicResponse, +from openbb_provider.utils.helpers import ( get_querystring, - request, ) from pydantic import BaseModel, NonNegativeInt, PositiveFloat, validator from requests.exceptions import SSLError @@ -18,10 +18,41 @@ from requests.exceptions import SSLError T = TypeVar("T", bound=BaseModel) -def get_data(url: str) -> Union[list, dict]: +class BasicResponse: + def __init__(self, response: StringIO): + # Find a way to get the status code + self.status_code = 200 + response.seek(0) + self.text = response.read() + + def json(self) -> dict: + return json.loads(self.text) + + +def request(url: str) -> BasicResponse: + """ + Request function for PyScript. Pass in Method and make sure to await! + Parameters: + ----------- + url: str + URL to make request to + + Return: + ------- + response: BasicRequest + BasicRequest object with status_code and text attributes + """ + # pylint: disable=import-outside-toplevel + from pyodide.http import open_url + + response = open_url(url) + return BasicResponse(response) + + +def get_data(url: str, **kwargs: Any) -> Union[list, dict]: """Get data from FMP endpoint.""" try: - r: Union[requests.Response, BasicResponse] = requests.get(url, timeout=10) + r: Union[requests.Response, BasicResponse] = helpers.make_request(url, **kwargs) except SSLError: r = request(url) if r.status_code == 404: @@ -69,14 +100,14 @@ def create_url( The querystring. """ - the_dict = {} if not query else query.dict() + the_dict = {} if not query else query.dict(by_alias=True) query_string = get_querystring(the_dict, exclude or []) base_url = f"https://financialmodelingprep.com/api/v{version}/" return f"{base_url}{endpoint}?{query_string}&apikey={api_key}" def get_data_many( - url: str, to_schema: Type[T], sub_dict: Optional[str] = None + url: str, to_schema: Type[T], sub_dict: Optional[str] = None, **kwargs: Any ) -> List[T]: """Get data from FMP endpoint and convert to list of schemas. @@ -94,7 +125,7 @@ def get_data_many( List[T] The list of schemas. """ - data = get_data(url) + data = get_data(url, **kwargs) if sub_dict and isinstance(data, dict): data = data.get(sub_dict, []) if isinstance(data, dict): @@ -102,9 +133,9 @@ def get_data_many( return [to_schema(**d) for d in data] -def get_data_one(url: str, to_schema: Type[T]) -> T: +def get_data_one(url: str, to_schema: Type[T], **kwargs: Any) -> T: """Get data from FMP endpoint and convert to schema.""" - data = get_data(url) + data = get_data(url, **kwargs) if isinstance(data, list): if len(data) == 0: raise ValueError("Expected dict, got empty list") diff --git a/openbb_sdk/providers/fmp/pyproject.toml b/openbb_sdk/providers/fmp/pyproject.toml index cfc93c8d7a0..e9463d7aa27 100644 --- a/openbb_sdk/providers/fmp/pyproject.toml +++ b/openbb_sdk/providers/fmp/pyproject.toml @@ -15,4 +15,4 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.poetry.plugins."openbb_provider_extension"] -fmp = "openbb_fmp.provider:fmp_provider" +fmp = "openbb_fmp:fmp_provider" diff --git a/openbb_sdk/providers/fmp/tests/.gitkeep b/openbb_sdk/providers/fmp/tests/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/providers/fred/openbb_fred/__init__.py b/openbb_sdk/providers/fred/openbb_fred/__init__.py index e69de29bb2d..b9f8a9d174a 100644 --- a/openbb_sdk/providers/fred/openbb_fred/__init__.py +++ b/openbb_sdk/providers/fred/openbb_fred/__init__.py @@ -0,0 +1,14 @@ +"""FRED provider module.""" +from openbb_provider.abstract.provider import Provider + +from openbb_fred.models.cpi import FREDCPIFetcher + +fred_provider = Provider( + name="fred", + website="https://fred.stlouisfed.org/", + description="""Federal Reserve Economic Data is a database maintained by the + Research division of the Federal Reserve Bank of St. Louis that has more than + 816,000 economic time series from various sources.""", + required_credentials=["api_key"], + fetcher_dict={"CPI": FREDCPIFetcher}, +) diff --git a/openbb_sdk/providers/fred/openbb_fred/models/cpi.py b/openbb_sdk/providers/fred/openbb_fred/models/cpi.py index be16ff234e5..755f138cee9 100644 --- a/openbb_sdk/providers/fred/openbb_fred/models/cpi.py +++ b/openbb_sdk/providers/fred/openbb_fred/models/cpi.py @@ -1,61 +1,34 @@ """FRED CPI Fetcher.""" -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional from openbb_fred.utils.fred_base import Fred from openbb_fred.utils.fred_helpers import all_cpi_options from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.cpi import CPIData, CPIDataPoint, CPIQueryParams +from openbb_provider.standard_models.cpi import CPIData, CPIQueryParams class FREDCPIQueryParams(CPIQueryParams): - """CPI query. - When other provders are added, this will probably need less strict types - - Parameter - --------- - countries: List[CPI_COUNTRIES] - The country or countries you want to see. - units: List[CPI_UNITS] - The units you want to see, can be "growth_previous", "growth_same" or "index_2015". - frequency: List[CPI_FREQUENCY] - The frequency you want to see, either "annual", monthly" or "quarterly". - harmonized: bool - Whether you wish to obtain harmonized data. - start_date: Optional[date] - Start date, formatted YYYY-MM-DD - end_date: Optional[date] - End date, formatted YYYY-MM-DD - """ + """CPI query.""" class FREDCPIData(CPIData): """CPI data.""" -class FREDCPIFetcher(Fetcher[CPIQueryParams, CPIData, FREDCPIQueryParams, FREDCPIData]): +class FREDCPIFetcher(Fetcher[FREDCPIQueryParams, FREDCPIData]): + """FRED CPI Fetcher.""" + @staticmethod - def transform_query( - query: CPIQueryParams, extra_params: Optional[Dict] = None - ) -> FREDCPIQueryParams: - return FREDCPIQueryParams( - countries=query.countries, - units=query.units, - frequency=query.frequency, - harmonized=query.harmonized, - start_date=query.start_date, - end_date=query.end_date, - **extra_params if extra_params else {}, - ) + def transform_query(params: Dict[str, Any]) -> FREDCPIQueryParams: + return FREDCPIQueryParams(**params) @staticmethod def extract_data( - query: FREDCPIQueryParams, credentials: Optional[Dict[str, str]] - ) -> FREDCPIData: - if credentials: - api_key = credentials.get("fred_api_key") + query: FREDCPIQueryParams, credentials: Optional[Dict[str, str]], **kwargs: Any + ) -> List[Dict[str, List[FREDCPIData]]]: + api_key = credentials.get("fred_api_key") if credentials else "" all_options = all_cpi_options(query.harmonized) @@ -63,20 +36,20 @@ class FREDCPIFetcher(Fetcher[CPIQueryParams, CPIData, FREDCPIQueryParams, FREDCP step_2 = [x for x in step_1 if x["units"] == query.units] step_3 = [x for x in step_2 if x["frequency"] == query.frequency] - if not step_3: - FREDCPIData(data={}) - series_dict = {} - fred = Fred(api_key) for item in step_3: loc = f"{item['country']}-{item['frequency']}-{item['units']}" - temp = fred.get_series(item["series_id"], query.start_date, query.end_date) - clean_temp = [CPIDataPoint(**x) for x in temp] + temp = fred.get_series( + item["series_id"], query.start_date, query.end_date, **kwargs + ) + clean_temp = [FREDCPIData(**x) for x in temp] series_dict[loc] = clean_temp - return FREDCPIData(data=series_dict) + return [series_dict] @staticmethod - def transform_data(data: List[FREDCPIData]) -> List[CPIData]: - return data_transformer(data, CPIData) + def transform_data( + data: List[Dict[str, List[FREDCPIData]]] + ) -> List[Dict[str, List[FREDCPIData]]]: + return data diff --git a/openbb_sdk/providers/fred/openbb_fred/provider.py b/openbb_sdk/providers/fred/openbb_fred/provider.py deleted file mode 100644 index 9764a39eeef..00000000000 --- a/openbb_sdk/providers/fred/openbb_fred/provider.py +++ /dev/null @@ -1,15 +0,0 @@ -"""FRED provider module.""" - - -from openbb_provider.abstract.provider import Provider - -from openbb_fred.models.cpi import FREDCPIFetcher - -# mypy: disable-error-code="list-item" - -fred_provider = Provider( - name="fred", - description="Provider for FRED.", - required_credentials=["api_key"], - fetcher_list=[FREDCPIFetcher], -) diff --git a/openbb_sdk/providers/fred/openbb_fred/utils/fred_base.py b/openbb_sdk/providers/fred/openbb_fred/utils/fred_base.py index 637aad8ac88..f9c8098d10e 100644 --- a/openbb_sdk/providers/fred/openbb_fred/utils/fred_base.py +++ b/openbb_sdk/providers/fred/openbb_fred/utils/fred_base.py @@ -1,8 +1,8 @@ from datetime import date -from typing import Optional +from typing import Any, Optional from urllib.parse import urlencode -import requests +from openbb_provider import helpers root_url = "https://api.stlouisfed.org/fred" @@ -11,9 +11,9 @@ class Fred: def __init__(self, api_key: Optional[str]): self.api_key = api_key - def __fetch_data(self, url: str): + def __fetch_data(self, url: str, **kwargs: Any): full_url = f"{url}&api_key={self.api_key}&file_type=json" - response = requests.get(full_url, timeout=10) + response = helpers.make_request(full_url, **kwargs) return response.json() def get_series( @@ -50,8 +50,10 @@ class Fred: if end_date: url += "&observation_end=" + end_date.strftime("%Y-%m-%d") if kwargs.keys(): - url += "&" + urlencode(kwargs) - root = self.__fetch_data(url) + url += "&" + urlencode( + {k: v for k, v in kwargs.items() if k != "preferences"} + ) + root = self.__fetch_data(url, **kwargs) if root is None: raise ValueError("No data exists for series id: " + series_id) return root["observations"] diff --git a/openbb_sdk/providers/fred/openbb_fred/utils/fred_helpers.py b/openbb_sdk/providers/fred/openbb_fred/utils/fred_helpers.py index 08b13e6823f..2992406b7a5 100644 --- a/openbb_sdk/providers/fred/openbb_fred/utils/fred_helpers.py +++ b/openbb_sdk/providers/fred/openbb_fred/utils/fred_helpers.py @@ -1,63 +1,7 @@ import csv import os from pathlib import Path -from typing import List, Literal - -CPI_COUNTRIES = Literal[ - "australia", - "austria", - "belgium", - "brazil", - "bulgaria", - "canada", - "chile", - "china", - "croatia", - "cyprus", - "czech_republic", - "denmark", - "estonia", - "euro_area", - "finland", - "france", - "germany", - "greece", - "hungary", - "iceland", - "india", - "indonesia", - "ireland", - "israel", - "italy", - "japan", - "korea", - "latvia", - "lithuania", - "luxembourg", - "malta", - "mexico", - "netherlands", - "new_zealand", - "norway", - "poland", - "portugal", - "romania", - "russian_federation", - "slovak_republic", - "slovakia", - "slovenia", - "south_africa", - "spain", - "sweden", - "switzerland", - "turkey", - "united_kingdom", - "united_states", -] - -CPI_UNITS = Literal["growth_previous", "growth_same", "index_2015"] - -CPI_FREQUENCY = Literal["monthly", "quarterly", "annual"] +from typing import List def all_cpi_options(harmonized: bool = False) -> List[dict]: diff --git a/openbb_sdk/providers/fred/poetry.lock b/openbb_sdk/providers/fred/poetry.lock deleted file mode 100644 index 583148ff3cf..00000000000 --- a/openbb_sdk/providers/fred/poetry.lock +++ /dev/null @@ -1,245 +0,0 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. - -[[package]] -name = "certifi" -version = "2023.5.7" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, - {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.2.0" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, - {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, -] - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "openbb-provider" -version = "0.2.0" -description = "" -optional = false -python-versions = "^3.8" -files = [] -develop = false - -[package.dependencies] -pydantic = {version = "^1.10", extras = ["dotenv"]} -requests = "^2.31.0" -urllib3 = "<2.0.0" - -[package.source] -type = "directory" -url = "../../sdk/provider" - -[[package]] -name = "pydantic" -version = "1.10.11" -description = "Data validation and settings management using python type hints" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pydantic-1.10.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ff44c5e89315b15ff1f7fdaf9853770b810936d6b01a7bcecaa227d2f8fe444f"}, - {file = "pydantic-1.10.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a6c098d4ab5e2d5b3984d3cb2527e2d6099d3de85630c8934efcfdc348a9760e"}, - {file = "pydantic-1.10.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16928fdc9cb273c6af00d9d5045434c39afba5f42325fb990add2c241402d151"}, - {file = "pydantic-1.10.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0588788a9a85f3e5e9ebca14211a496409cb3deca5b6971ff37c556d581854e7"}, - {file = "pydantic-1.10.11-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9baf78b31da2dc3d3f346ef18e58ec5f12f5aaa17ac517e2ffd026a92a87588"}, - {file = "pydantic-1.10.11-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:373c0840f5c2b5b1ccadd9286782852b901055998136287828731868027a724f"}, - {file = "pydantic-1.10.11-cp310-cp310-win_amd64.whl", hash = "sha256:c3339a46bbe6013ef7bdd2844679bfe500347ac5742cd4019a88312aa58a9847"}, - {file = "pydantic-1.10.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:08a6c32e1c3809fbc49debb96bf833164f3438b3696abf0fbeceb417d123e6eb"}, - {file = "pydantic-1.10.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a451ccab49971af043ec4e0d207cbc8cbe53dbf148ef9f19599024076fe9c25b"}, - {file = "pydantic-1.10.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b02d24f7b2b365fed586ed73582c20f353a4c50e4be9ba2c57ab96f8091ddae"}, - {file = "pydantic-1.10.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f34739a89260dfa420aa3cbd069fbcc794b25bbe5c0a214f8fb29e363484b66"}, - {file = "pydantic-1.10.11-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e297897eb4bebde985f72a46a7552a7556a3dd11e7f76acda0c1093e3dbcf216"}, - {file = "pydantic-1.10.11-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d185819a7a059550ecb85d5134e7d40f2565f3dd94cfd870132c5f91a89cf58c"}, - {file = "pydantic-1.10.11-cp311-cp311-win_amd64.whl", hash = "sha256:4400015f15c9b464c9db2d5d951b6a780102cfa5870f2c036d37c23b56f7fc1b"}, - {file = "pydantic-1.10.11-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2417de68290434461a266271fc57274a138510dca19982336639484c73a07af6"}, - {file = "pydantic-1.10.11-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:331c031ba1554b974c98679bd0780d89670d6fd6f53f5d70b10bdc9addee1713"}, - {file = "pydantic-1.10.11-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8268a735a14c308923e8958363e3a3404f6834bb98c11f5ab43251a4e410170c"}, - {file = "pydantic-1.10.11-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:44e51ba599c3ef227e168424e220cd3e544288c57829520dc90ea9cb190c3248"}, - {file = "pydantic-1.10.11-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d7781f1d13b19700b7949c5a639c764a077cbbdd4322ed505b449d3ca8edcb36"}, - {file = "pydantic-1.10.11-cp37-cp37m-win_amd64.whl", hash = "sha256:7522a7666157aa22b812ce14c827574ddccc94f361237ca6ea8bb0d5c38f1629"}, - {file = "pydantic-1.10.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc64eab9b19cd794a380179ac0e6752335e9555d214cfcb755820333c0784cb3"}, - {file = "pydantic-1.10.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8dc77064471780262b6a68fe67e013298d130414d5aaf9b562c33987dbd2cf4f"}, - {file = "pydantic-1.10.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe429898f2c9dd209bd0632a606bddc06f8bce081bbd03d1c775a45886e2c1cb"}, - {file = "pydantic-1.10.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:192c608ad002a748e4a0bed2ddbcd98f9b56df50a7c24d9a931a8c5dd053bd3d"}, - {file = "pydantic-1.10.11-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ef55392ec4bb5721f4ded1096241e4b7151ba6d50a50a80a2526c854f42e6a2f"}, - {file = "pydantic-1.10.11-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:41e0bb6efe86281623abbeeb0be64eab740c865388ee934cd3e6a358784aca6e"}, - {file = "pydantic-1.10.11-cp38-cp38-win_amd64.whl", hash = "sha256:265a60da42f9f27e0b1014eab8acd3e53bd0bad5c5b4884e98a55f8f596b2c19"}, - {file = "pydantic-1.10.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:469adf96c8e2c2bbfa655fc7735a2a82f4c543d9fee97bd113a7fb509bf5e622"}, - {file = "pydantic-1.10.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e6cbfbd010b14c8a905a7b10f9fe090068d1744d46f9e0c021db28daeb8b6de1"}, - {file = "pydantic-1.10.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abade85268cc92dff86d6effcd917893130f0ff516f3d637f50dadc22ae93999"}, - {file = "pydantic-1.10.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9738b0f2e6c70f44ee0de53f2089d6002b10c33264abee07bdb5c7f03038303"}, - {file = "pydantic-1.10.11-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:787cf23e5a0cde753f2eabac1b2e73ae3844eb873fd1f5bdbff3048d8dbb7604"}, - {file = "pydantic-1.10.11-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:174899023337b9fc685ac8adaa7b047050616136ccd30e9070627c1aaab53a13"}, - {file = "pydantic-1.10.11-cp39-cp39-win_amd64.whl", hash = "sha256:1954f8778489a04b245a1e7b8b22a9d3ea8ef49337285693cf6959e4b757535e"}, - {file = "pydantic-1.10.11-py3-none-any.whl", hash = "sha256:008c5e266c8aada206d0627a011504e14268a62091450210eda7c07fabe6963e"}, - {file = "pydantic-1.10.11.tar.gz", hash = "sha256:f66d479cf7eb331372c470614be6511eae96f1f120344c25f3f9bb59fb1b5528"}, -] - -[package.dependencies] -python-dotenv = {version = ">=0.10.4", optional = true, markers = "extra == \"dotenv\""} -typing-extensions = ">=4.2.0" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] - -[[package]] -name = "python-dotenv" -version = "1.0.0" -description = "Read key-value pairs from a .env file and set them as environment variables" -optional = false -python-versions = ">=3.8" -files = [ - {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, - {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.7" -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, -] - -[[package]] -name = "urllib3" -version = "1.26.16" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -files = [ - {file = "urllib3-1.26.16-py2.py3-none-any.whl", hash = "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f"}, - {file = "urllib3-1.26.16.tar.gz", hash = "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.8" -content-hash = "26269b8a989dcc6ac67961b7a43d485d231c8ac878265ac90f310ced959fc532" diff --git a/openbb_sdk/providers/fred/pyproject.toml b/openbb_sdk/providers/fred/pyproject.toml index 7cc148b5805..bd05f8aef34 100644 --- a/openbb_sdk/providers/fred/pyproject.toml +++ b/openbb_sdk/providers/fred/pyproject.toml @@ -15,4 +15,4 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.poetry.plugins."openbb_provider_extension"] -fred = "openbb_fred.provider:fred_provider" +fred = "openbb_fred:fred_provider" diff --git a/openbb_sdk/providers/fred/tests/.gitkeep b/openbb_sdk/providers/fred/tests/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/providers/polygon/openbb_polygon/__init__.py b/openbb_sdk/providers/polygon/openbb_polygon/__init__.py index e69de29bb2d..042c59d2358 100644 --- a/openbb_sdk/providers/polygon/openbb_polygon/__init__.py +++ b/openbb_sdk/providers/polygon/openbb_polygon/__init__.py @@ -0,0 +1,32 @@ +"""Polygon provider module.""" +from openbb_provider.abstract.provider import Provider + +from openbb_polygon.models.balance_sheet import PolygonBalanceSheetFetcher +from openbb_polygon.models.cash_flow import PolygonCashFlowStatementFetcher +from openbb_polygon.models.crypto_eod import PolygonCryptoEODFetcher +from openbb_polygon.models.forex_eod import PolygonForexEODFetcher +from openbb_polygon.models.forex_pairs import PolygonForexPairsFetcher +from openbb_polygon.models.income_statement import PolygonIncomeStatementFetcher +from openbb_polygon.models.major_indices_eod import PolygonMajorIndicesEODFetcher +from openbb_polygon.models.stock_eod import PolygonStockEODFetcher +from openbb_polygon.models.stock_news import PolygonStockNewsFetcher + +polygon_provider = Provider( + name="polygon", + website="https://polygon.io/", + description="""The Polygon.io Stocks API provides REST endpoints that let you query + the latest market data from all US stock exchanges. You can also find data on + company financials, stock market holidays, corporate actions, and more.""", + required_credentials=["api_key"], + fetcher_dict={ + "StockEOD": PolygonStockEODFetcher, + "StockNews": PolygonStockNewsFetcher, + "BalanceSheet": PolygonBalanceSheetFetcher, + "IncomeStatement": PolygonIncomeStatementFetcher, + "CashFlowStatement": PolygonCashFlowStatementFetcher, + "CryptoEOD": PolygonCryptoEODFetcher, + "MajorIndicesEOD": PolygonMajorIndicesEODFetcher, + "ForexEOD": PolygonForexEODFetcher, + "ForexPairs": PolygonForexPairsFetcher, + }, +) diff --git a/openbb_sdk/providers/polygon/openbb_polygon/models/balance_sheet.py b/openbb_sdk/providers/polygon/openbb_polygon/models/balance_sheet.py index 2835de250e8..44bc9c1ceb1 100644 --- a/openbb_sdk/providers/polygon/openbb_polygon/models/balance_sheet.py +++ b/openbb_sdk/providers/polygon/openbb_polygon/models/balance_sheet.py @@ -1,93 +1,88 @@ -from datetime import date as dateType -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer, get_querystring -from openbb_provider.models.balance_sheet import ( +from openbb_provider.standard_models.balance_sheet import ( BalanceSheetData, - BalanceSheetQueryParams, ) -from pydantic import Field +from openbb_provider.utils.helpers import get_querystring +from pydantic import validator from openbb_polygon.utils.helpers import get_data from openbb_polygon.utils.types import PolygonFundamentalQueryParams class PolygonBalanceSheetQueryParams(PolygonFundamentalQueryParams): - __doc__ = PolygonFundamentalQueryParams.__doc__ + """Polygon Fundamental QueryParams. + + Source: https://polygon.io/docs/stocks#!/get_vx_reference_financials + """ -class PolygonBalanceSheetData(Data): - cik: Optional[str] - start_date: dateType = Field(alias="date") - assets: Optional[int] - current_assets: Optional[int] - current_liabilities: Optional[int] - equity: Optional[int] = Field(alias="total_equity") - equity_attributable_to_noncontrolling_interest: Optional[int] - equity_attributable_to_parent: Optional[int] - liabilities: Optional[int] - liabilities_and_equity: Optional[int] = Field( - alias="total_liabilities_and_stockholders_equity" - ) - noncurrent_assets: Optional[int] - noncurrent_liabilities: Optional[int] +class PolygonBalanceSheetData(BalanceSheetData): + """Return Balance Sheet Data.""" + + class Config: + fields = { + "date": "start_date", + "equity": "total_equity", + "total_liabilities_and_stockholders_equity": "liabilities_and_equity", + "minority_interest": "equity_attributable_to_noncontrolling_interest", + } + + @validator("symbol", pre=True, check_fields=False) + def symbol_from_tickers(cls, v): + if isinstance(v, list): + return ",".join(v) + return v class PolygonBalanceSheetFetcher( Fetcher[ - BalanceSheetQueryParams, - BalanceSheetData, PolygonBalanceSheetQueryParams, PolygonBalanceSheetData, ] ): @staticmethod - def transform_query( - query: BalanceSheetQueryParams, extra_params: Optional[Dict] = None - ) -> PolygonBalanceSheetQueryParams: - period = "annual" if query.period == "annually" else "quarterly" - return PolygonBalanceSheetQueryParams( - symbol=query.symbol, period=period, **extra_params if extra_params else {} # type: ignore - ) + def transform_query(params: Dict[str, Any]) -> PolygonBalanceSheetQueryParams: + return PolygonBalanceSheetQueryParams(**params) @staticmethod def extract_data( - query: PolygonBalanceSheetQueryParams, credentials: Optional[Dict[str, str]] + query: PolygonBalanceSheetQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[PolygonBalanceSheetData]: - if credentials: - api_key = credentials.get("polygon_api_key") + api_key = credentials.get("polygon_api_key") if credentials else "" + + query.period = "annual" if query.period == "annually" else "quarter" base_url = "https://api.polygon.io/vX/reference/financials" - query_string = get_querystring(query.dict(), []) + query_string = get_querystring(query.dict(by_alias=True), []) request_url = f"{base_url}?{query_string}&apiKey={api_key}" - data = get_data(request_url)["results"] + data = get_data(request_url, **kwargs)["results"] if len(data) == 0: raise RuntimeError("No balance sheet found") + FIELDS = [ + "assets", + "current_assets", + "current_liabilities", + "equity", + "equity_attributable_to_noncontrolling_interest", + "equity_attributable_to_parent", + "liabilities", + "liabilities_and_equity", + "noncurrent_assets", + "noncurrent_liabilities", + ] + to_return = [] for item in data: - new = {"start_date": item["start_date"]} - new["cik"] = item["cik"] - bs = item["financials"]["balance_sheet"] - # TODO: Unpack this dynamically to avoid hardcoding. - new["assets"] = bs["assets"].get("value") - new["current_assets"] = bs["current_assets"].get("value") - new["current_liabilities"] = bs["current_liabilities"].get("value") - new["equity"] = bs["equity"].get("value") - new["equity_attributable_to_noncontrolling_interest"] = bs[ - "equity_attributable_to_noncontrolling_interest" - ].get("value") - new["equity_attributable_to_parent"] = bs[ - "equity_attributable_to_parent" - ].get("value") - new["equity"] = bs["equity"].get("value") - new["liabilities"] = bs["liabilities"].get("value") - new["liabilities_and_equity"] = bs["liabilities_and_equity"].get("value") - new["noncurrent_assets"] = bs["noncurrent_assets"].get("value") - new["noncurrent_liabilities"] = bs["noncurrent_liabilities"].get("value") + new = {"start_date": item["start_date"], "cik": item["cik"]} + if bs := item["financials"]["balance_sheet"]: + for field in FIELDS: + new[field] = bs[field].get("value", 0) to_return.append(PolygonBalanceSheetData(**new)) return to_return @@ -95,5 +90,5 @@ class PolygonBalanceSheetFetcher( @staticmethod def transform_data( data: List[PolygonBalanceSheetData], - ) -> List[BalanceSheetData]: - return data_transformer(data, BalanceSheetData) + ) -> List[PolygonBalanceSheetData]: + return data diff --git a/openbb_sdk/providers/polygon/openbb_polygon/models/cash_flow.py b/openbb_sdk/providers/polygon/openbb_polygon/models/cash_flow.py index 6c82e04d9fc..d0ae9cb53dd 100644 --- a/openbb_sdk/providers/polygon/openbb_polygon/models/cash_flow.py +++ b/openbb_sdk/providers/polygon/openbb_polygon/models/cash_flow.py @@ -1,17 +1,11 @@ -from datetime import ( - date as dateType, - datetime, -) -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer, get_querystring -from openbb_provider.models.cash_flows import ( +from openbb_provider.standard_models.cash_flows import ( CashFlowStatementData, - CashFlowStatementQueryParams, ) -from pydantic import Field +from openbb_provider.utils.helpers import get_querystring +from pydantic import validator from openbb_polygon.utils.helpers import get_data from openbb_polygon.utils.types import PolygonFundamentalQueryParams @@ -23,87 +17,64 @@ class PolygonCashFlowStatementQueryParams(PolygonFundamentalQueryParams): __doc__ = PolygonFundamentalQueryParams.__doc__ -class PolygonCashFlowStatementData(Data): - start_date: dateType = Field(alias="date") - tickers: Optional[List[str]] = Field(alias="symbol") - cik: Optional[str] - filing_date: Optional[dateType] - acceptance_datetime: Optional[datetime] - fiscal_period: Optional[str] = Field(alias="period") - net_cash_flow: int - net_cash_flow_from_financing_activities: int - net_cash_flow_from_investing_activities: int - net_cash_flow_from_operating_activities: int - net_cash_flow_continuing: int - net_cash_flow_from_financing_activities_continuing: int - net_cash_flow_from_investing_activities_continuing: int - net_cash_flow_from_operating_activities_continuing: int +class PolygonCashFlowStatementData(CashFlowStatementData): + """Cash Flow Statement Data.""" + + class Config: + fields = {"date": "start_date"} + + @validator("symbol", pre=True, check_fields=False) + def symbol_from_tickers(cls, v): + if isinstance(v, list): + return ",".join(v) + return v class PolygonCashFlowStatementFetcher( Fetcher[ - CashFlowStatementQueryParams, - CashFlowStatementData, PolygonCashFlowStatementQueryParams, PolygonCashFlowStatementData, ] ): @staticmethod - def transform_query( - query: CashFlowStatementQueryParams, extra_params: Optional[Dict] = None - ) -> PolygonCashFlowStatementQueryParams: - period = "annual" if query.period == "annually" else "quarterly" - return PolygonCashFlowStatementQueryParams( - symbol=query.symbol, period=period, **extra_params if extra_params else {} # type: ignore - ) + def transform_query(params: Dict[str, Any]) -> PolygonCashFlowStatementQueryParams: + return PolygonCashFlowStatementQueryParams(**params) @staticmethod def extract_data( query: PolygonCashFlowStatementQueryParams, credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[PolygonCashFlowStatementData]: - if credentials: - api_key = credentials.get("polygon_api_key") + api_key = credentials.get("polygon_api_key") if credentials else "" + + query.period = "annual" if query.period == "annually" else "quarter" base_url = "https://api.polygon.io/vX/reference/financials" - query_string = get_querystring(query.dict(), []) + query_string = get_querystring(query.dict(by_alias=True), []) request_url = f"{base_url}?{query_string}&apiKey={api_key}" - data = get_data(request_url)["results"] + data = get_data(request_url, **kwargs)["results"] if len(data) == 0: raise RuntimeError("No Cash Flow Statement found") + FIELDS = [ + "net_cash_flow_from_financing_activities", + "net_cash_flow_from_investing_activities", + "net_cash_flow_from_operating_activities", + "net_cash_flow_continuing", + "net_cash_flow_from_financing_activities_continuing", + "net_cash_flow_from_investing_activities_continuing", + "net_cash_flow_from_operating_activities_continuing", + "net_cash_flow", + ] + to_return = [] for item in data: - new = {"acceptance_datetime": item.get("acceptance_datetime")} - new["start_date"] = item["start_date"] - new["filing_date"] = item.get("filing_date") - new["cik"] = item["cik"] - new["fiscal_period"] = item["fiscal_period"] - new["tickers"] = item["tickers"] - cf = item["financials"]["cash_flow_statement"] - new["net_cash_flow_from_financing_activities"] = cf[ - "net_cash_flow_from_financing_activities" - ].get("value") - new["net_cash_flow_from_investing_activities"] = cf[ - "net_cash_flow_from_investing_activities" - ].get("value") - new["net_cash_flow_from_operating_activities"] = cf[ - "net_cash_flow_from_operating_activities" - ].get("value") - new["net_cash_flow_continuing"] = cf["net_cash_flow_continuing"].get( - "value" - ) - new["net_cash_flow_from_financing_activities_continuing"] = cf[ - "net_cash_flow_from_financing_activities_continuing" - ].get("value") - new["net_cash_flow_from_investing_activities_continuing"] = cf[ - "net_cash_flow_from_investing_activities_continuing" - ].get("value") - new["net_cash_flow_from_operating_activities_continuing"] = cf[ - "net_cash_flow_from_operating_activities_continuing" - ].get("value") - new["net_cash_flow"] = cf["net_cash_flow"].get("value") + new = {"start_date": item["start_date"], "cik": item["cik"]} + if cf := item["financials"]["cash_flow_statement"]: + for field in FIELDS: + new[field] = cf[field].get("value", 0) to_return.append(PolygonCashFlowStatementData(**new)) return to_return @@ -112,5 +83,4 @@ class PolygonCashFlowStatementFetcher( def transform_data( data: List[PolygonCashFlowStatementData], ) -> List[CashFlowStatementData]: - processors = {"tickers": lambda x: "" if not x else ",".join(x)} - return data_transformer(data, CashFlowStatementData, processors) + return data diff --git a/openbb_sdk/providers/polygon/openbb_polygon/models/crypto_eod.py b/openbb_sdk/providers/polygon/openbb_polygon/models/crypto_eod.py index b79c6a4c469..c64df18f691 100644 --- a/openbb_sdk/providers/polygon/openbb_polygon/models/crypto_eod.py +++ b/openbb_sdk/providers/polygon/openbb_polygon/models/crypto_eod.py @@ -2,96 +2,107 @@ from datetime import datetime, timedelta -from typing import Dict, List, Optional +from typing import Any, Dict, List, Literal, Optional from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.crypto_eod import CryptoEODData, CryptoEODQueryParams -from pydantic import Field, NonNegativeFloat, PositiveFloat, PositiveInt +from openbb_provider.standard_models.crypto_eod import ( + CryptoEODData, + CryptoEODQueryParams, +) +from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS +from pydantic import Field, PositiveInt, validator from openbb_polygon.utils.helpers import get_data -from openbb_polygon.utils.types import BaseStockData, BaseStockQueryParams -class PolygonCryptoEODQueryParams(BaseStockQueryParams): - """Polygon crypto end of day query. +class PolygonCryptoEODQueryParams(CryptoEODQueryParams): + """Polygon crypto end of day Query. Source: https://polygon.io/docs/crypto/get_v2_aggs_ticker__cryptoticker__range__multiplier___timespan___from___to - - Parameters - ---------- - symbol : str - The symbol symbol of the stocks to fetch. - start_date : Union[date, datetime] - The start date of the query. - end_date : Union[date, datetime] - The end date of the query. - timespan : Timespan, optional - The timespan of the query, by default Timespan.day - sort : Literal["asc", "desc"], optional - The sort order of the query, by default "desc" - limit : PositiveInt, optional - The limit of the query, by default 49999 - adjusted : bool, optional - Whether the query is adjusted, by default True - multiplier : PositiveInt, optional - The multiplier of the query, by default 1 """ + timespan: Literal[ + "minute", "hour", "day", "week", "month", "quarter", "year" + ] = Field(default="day", description="The timespan of the data.") + sort: Literal["asc", "desc"] = Field( + default="desc", description="Sort order of the data." + ) + limit: PositiveInt = Field( + default=49999, description=QUERY_DESCRIPTIONS.get("limit", "") + ) + adjusted: bool = Field(default=True, description="Whether the data is adjusted.") + multiplier: PositiveInt = Field( + default=1, description="The multiplier of the timespan." + ) -class PolygonCryptoEODData(BaseStockData): - v: NonNegativeFloat = Field(alias="volume") - n: PositiveInt - vw: PositiveFloat + +class PolygonCryptoEODData(CryptoEODData): + """Polygon crypto end of day Data.""" + + class Config: + fields = { + "date": "t", + "open": "o", + "high": "h", + "low": "l", + "close": "c", + "volume": "v", + "vwap": "vw", + } + + n: PositiveInt = Field( + description="The number of transactions for the symbol in the time period." + ) + + @validator("t", pre=True, check_fields=False) + def time_validate(cls, v): # pylint: disable=E0213 + return datetime.fromtimestamp(v / 1000) class PolygonCryptoEODFetcher( Fetcher[ - CryptoEODQueryParams, - CryptoEODData, PolygonCryptoEODQueryParams, PolygonCryptoEODData, ] ): @staticmethod - def transform_query( - query: CryptoEODQueryParams, extra_params: Optional[Dict] = None - ) -> PolygonCryptoEODQueryParams: - now = datetime.now() - start_date = query.start_date if query.start_date else now - timedelta(days=7) - end_date = query.end_date if query.end_date else now - return PolygonCryptoEODQueryParams( - symbol=query.symbol, - start_date=start_date, - end_date=end_date, - **extra_params if extra_params else {}, - ) + def transform_query(params: Dict[str, Any]) -> PolygonCryptoEODQueryParams: + now = datetime.now().date() + transformed_params = params + if params.get("start_date") is None: + transformed_params["start_date"] = now - timedelta(days=7) + + if params.get("end_date") is None: + transformed_params["end_date"] = now + + return PolygonCryptoEODQueryParams(**transformed_params) @staticmethod def extract_data( - query: PolygonCryptoEODQueryParams, credentials: Optional[Dict[str, str]] + query: PolygonCryptoEODQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[PolygonCryptoEODData]: - if credentials: - api_key = credentials.get("polygon_api_key") + api_key = credentials.get("polygon_api_key") if credentials else "" request_url = ( f"https://api.polygon.io/v2/aggs/ticker/" - f"X:{query.stocksTicker.upper()}/range/1/{str(query.timespan.value)}/" + f"X:{query.symbol}/range/1/{str(query.timespan)}/" f"{query.start_date}/{query.end_date}?adjusted={query.adjusted}" f"&sort={query.sort}&limit={query.limit}&multiplier={query.multiplier}" f"&apiKey={api_key}" ) - data = get_data(request_url) + data = get_data(request_url, **kwargs) if isinstance(data, list): raise ValueError("Expected a dict, got a list") - if "results" not in data.keys() or len(data["results"]) == 0: + if "results" not in data or len(data["results"]) == 0: raise RuntimeError("No results found. Please change your query parameters.") data = data["results"] return [PolygonCryptoEODData(**d) for d in data] @staticmethod - def transform_data(data: List[PolygonCryptoEODData]) -> List[CryptoEODData]: - return data_transformer(data, CryptoEODData) + def transform_data(data: List[PolygonCryptoEODData]) -> List[PolygonCryptoEODData]: + return data diff --git a/openbb_sdk/providers/polygon/openbb_polygon/models/crypto_price.py b/openbb_sdk/providers/polygon/openbb_polygon/models/crypto_price.py deleted file mode 100644 index d02d9f9518e..00000000000 --- a/openbb_sdk/providers/polygon/openbb_polygon/models/crypto_price.py +++ /dev/null @@ -1,94 +0,0 @@ -"""Polygon Crypto Price fetcher.""" - - -from datetime import datetime -from typing import Dict, List, Optional - -from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.crypto_price import CryptoPriceData, CryptoPriceQueryParams -from pydantic import Field, NonNegativeFloat, PositiveFloat, PositiveInt - -from openbb_polygon.utils.helpers import get_data -from openbb_polygon.utils.types import BaseStockData, BaseStockQueryParams - - -class PolygonCryptoPriceQueryParams(BaseStockQueryParams): - """Polygon crypto price query. - - Source: https://polygon.io/docs/crypto/get_v2_aggs_ticker__cryptoticker__range__multiplier___timespan___from___to - - Parameters - ---------- - stocksTicker : str - The ticker symbol of the stocks to fetch. - start_date : Union[date, datetime] - The start date of the query. - end_date : Union[date, datetime] - The end date of the query. - timespan : Timespan, optional - The timespan of the query, by default Timespan.day - sort : Literal["asc", "desc"], optional - The sort order of the query, by default "desc" - limit : PositiveInt, optional - The limit of the query, by default 49999 - adjusted : bool, optional - Whether the query is adjusted, by default True - multiplier : PositiveInt, optional - The multiplier of the query, by default 1 - """ - - -class PolygonCryptoPriceData(BaseStockData): - v: NonNegativeFloat = Field(alias="volume") - n: PositiveInt - vw: PositiveFloat - - -class PolygonCryptoPriceFetcher( - Fetcher[ - CryptoPriceQueryParams, - CryptoPriceData, - PolygonCryptoPriceQueryParams, - PolygonCryptoPriceData, - ] -): - @staticmethod - def transform_query( - query: CryptoPriceQueryParams, extra_params: Optional[Dict] = None - ) -> PolygonCryptoPriceQueryParams: - return PolygonCryptoPriceQueryParams( - symbol=query.symbol, - start_date=query.start_date, - end_date=query.end_date if query.end_date else datetime.now(), - **extra_params if extra_params else {}, - ) - - @staticmethod - def extract_data( - query: PolygonCryptoPriceQueryParams, credentials: Optional[Dict[str, str]] - ) -> List[PolygonCryptoPriceData]: - if credentials: - api_key = credentials.get("polygon_api_key") - - request_url = ( - f"https://api.polygon.io/v2/aggs/ticker/" - f"X:{query.stocksTicker.upper()}/range/1/{str(query.timespan.value)}/" - f"{query.start_date}/{query.end_date}?adjusted={query.adjusted}" - f"&sort={query.sort}&limit={query.limit}&multiplier={query.multiplier}" - f"&apiKey={api_key}" - ) - - data = get_data(request_url) - if isinstance(data, list): - raise ValueError("Expected a dict, got a list") - - if "results" not in data.keys() or len(data["results"]) == 0: - raise RuntimeError("No results found. Please change your query parameters.") - - data = data["results"] - return [PolygonCryptoPriceData(**d) for d in data] - - @staticmethod - def transform_data(data: List[PolygonCryptoPriceData]) -> List[CryptoPriceData]: - return data_transformer(data, CryptoPriceData) diff --git a/openbb_sdk/providers/polygon/openbb_polygon/models/forex_eod.py b/openbb_sdk/providers/polygon/openbb_polygon/models/forex_eod.py index 7954927b500..bc1c7d6d9ef 100644 --- a/openbb_sdk/providers/polygon/openbb_polygon/models/forex_eod.py +++ b/openbb_sdk/providers/polygon/openbb_polygon/models/forex_eod.py @@ -1,105 +1,104 @@ """Polygon forex end of day fetcher.""" -from datetime import date, datetime, timedelta -from typing import Dict, List, Literal, Optional, Union +from datetime import datetime, timedelta +from typing import Any, Dict, List, Literal, Optional -from openbb_provider.abstract.data import QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.forex_eod import ForexEODData, ForexEODQueryParams -from pydantic import Field, NonNegativeFloat, PositiveFloat, PositiveInt +from openbb_provider.standard_models.forex_eod import ForexEODData, ForexEODQueryParams +from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS +from pydantic import Field, PositiveInt, validator from openbb_polygon.utils.helpers import get_data -from openbb_polygon.utils.types import BaseStockData, Timespan -class PolygonForexEODQueryParams(QueryParams): - """Polygon forex end of day query. +class PolygonForexEODQueryParams(ForexEODQueryParams): + """Polygon forex end of day Query. Source: https://polygon.io/docs/forex/get_v2_aggs_ticker__forexticker__range__multiplier___timespan___from___to - - Parameters - ---------- - stocksTicker : str - The ticker symbol of the stocks to fetch. - start_date : Union[date, datetime], optional - The start date of the query. - end_date : Union[date, datetime], optional - The end date of the query. - timespan : Timespan, optional - The timespan of the query, by default Timespan.day - sort : Literal["asc", "desc"], optional - The sort order of the query, by default "desc" - limit : PositiveInt, optional - The limit of the query, by default 49999 - adjusted : bool, optional - Whether the query is adjusted, by default True - multiplier : PositiveInt, optional - The multiplier of the query, by default 1 """ - stocksTicker: str = Field(alias="symbol") - start_date: Optional[Union[date, datetime]] - end_date: Optional[Union[date, datetime]] - timespan: Optional[Timespan] = Field(default=Timespan.day) - sort: Optional[Literal["asc", "desc"]] = Field(default="desc") - limit: Optional[PositiveInt] = Field(default=49999) - adjusted: Optional[bool] = Field(default=True) - multiplier: Optional[PositiveInt] = Field(default=1) + timespan: Literal[ + "minute", "hour", "day", "week", "month", "quarter", "year" + ] = Field(default="day", description="The timespan of the data.") + sort: Literal["asc", "desc"] = Field( + default="desc", description="Sort order of the data." + ) + limit: PositiveInt = Field( + default=49999, description=QUERY_DESCRIPTIONS.get("limit", "") + ) + adjusted: bool = Field(default=True, description="Whether the data is adjusted.") + multiplier: PositiveInt = Field( + default=1, description="The multiplier of the timespan." + ) -class PolygonForexEODData(BaseStockData): - v: NonNegativeFloat = Field(alias="volume") - n: PositiveInt - vw: Optional[PositiveFloat] +class PolygonForexEODData(ForexEODData): + """Polygon forex end of day Data.""" + + class Config: + fields = { + "date": "t", + "open": "o", + "high": "h", + "low": "l", + "close": "c", + "volume": "v", + "vwap": "vw", + } + + n: PositiveInt = Field( + description="The number of transactions for the symbol in the time period." + ) + + @validator("t", pre=True, check_fields=False) + def time_validate(cls, v): # pylint: disable=E0213 + return datetime.fromtimestamp(v / 1000) class PolygonForexEODFetcher( Fetcher[ - ForexEODQueryParams, - ForexEODData, PolygonForexEODQueryParams, PolygonForexEODData, ] ): @staticmethod - def transform_query( - query: ForexEODQueryParams, extra_params: Optional[Dict] = None - ) -> PolygonForexEODQueryParams: - return PolygonForexEODQueryParams( - symbol=query.symbol, - **extra_params or {}, - ) + def transform_query(params: Dict[str, Any]) -> PolygonForexEODQueryParams: + now = datetime.now().date() + transformed_params = params + if params.get("start_date") is None: + transformed_params["start_date"] = now - timedelta(days=7) + + if params.get("end_date") is None: + transformed_params["end_date"] = now + return PolygonForexEODQueryParams(**transformed_params) @staticmethod def extract_data( - query: PolygonForexEODQueryParams, credentials: Optional[Dict[str, str]] + query: PolygonForexEODQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[PolygonForexEODData]: - if credentials: - api_key = credentials.get("polygon_api_key") + api_key = credentials.get("polygon_api_key") if credentials else "" - now = datetime.now() - start_date = query.start_date or (now - timedelta(days=5)).date() - end_date = query.end_date or now.date() request_url = ( f"https://api.polygon.io/v2/aggs/ticker/" - f"C:{query.stocksTicker.upper()}/range/1/{query.timespan._value_}/" - f"{start_date}/{end_date}?adjusted={query.adjusted}" + f"C:{query.symbol}/range/1/{query.timespan}/" + f"{query.start_date}/{query.end_date}?adjusted={query.adjusted}" f"&sort={query.sort}&limit={query.limit}&multiplier={query.multiplier}" f"&apiKey={api_key}" ) - data = get_data(request_url) + data = get_data(request_url, **kwargs) if isinstance(data, list): raise ValueError("Expected a dict, got a list") - if "results" not in data.keys() or len(data["results"]) == 0: + if "results" not in data or len(data["results"]) == 0: raise RuntimeError("No results found. Please change your query parameters.") data = data["results"] return [PolygonForexEODData(**d) for d in data] @staticmethod - def transform_data(data: List[PolygonForexEODData]) -> List[ForexEODData]: - return data_transformer(data, ForexEODData) + def transform_data(data: List[PolygonForexEODData]) -> List[PolygonForexEODData]: + return data diff --git a/openbb_sdk/providers/polygon/openbb_polygon/models/forex_pairs.py b/openbb_sdk/providers/polygon/openbb_polygon/models/forex_pairs.py new file mode 100644 index 00000000000..8cd51afb5c6 --- /dev/null +++ b/openbb_sdk/providers/polygon/openbb_polygon/models/forex_pairs.py @@ -0,0 +1,138 @@ +"""Polygon available pairs fetcher.""" + + +from datetime import ( + date as dateType, + datetime, +) +from typing import Any, Dict, List, Literal, Optional + +from openbb_provider.abstract.fetcher import Fetcher +from openbb_provider.standard_models.forex_pairs import ( + ForexPairsData, + ForexPairsQueryParams, +) +from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS +from pydantic import Field, PositiveInt, validator + +from openbb_polygon.utils.helpers import get_data + + +class PolygonForexPairsQueryParams(ForexPairsQueryParams): + """Polygon available pairs Query. + + Source: https://polygon.io/docs/forex/get_v3_reference_tickers + """ + + symbol: Optional[str] = Field(description="Symbol of the pair to search.") + date: Optional[dateType] = Field( + default=datetime.now().date(), description=QUERY_DESCRIPTIONS.get("date", "") + ) + search: Optional[str] = Field( + default="", + description="Search for terms within the ticker and/or company name.", + ) + active: Optional[Literal[True, False]] = Field( + default=True, + description="Specify if the tickers returned should be actively traded on the queried date.", + ) + order: Optional[Literal["asc", "desc"]] = Field( + default="asc", description="Order data by ascending or descending." + ) + sort: Optional[ + Literal[ + "ticker", + "name", + "market", + "locale", + "currency_symbol", + "currency_name", + "base_currency_symbol", + "base_currency_name", + "last_updated_utc", + "delisted_utc", + ] + ] = Field(default="", description="Sort field used for ordering.") + limit: Optional[PositiveInt] = Field( + default=1000, description=QUERY_DESCRIPTIONS.get("limit", "") + ) + + +class PolygonForexPairsData(ForexPairsData): + """Polygon available pairs Data.""" + + market: str = Field(description="The name of the trading market. Always 'fx'.") + locale: str = Field(description="The locale of the currency pair.") + currency_symbol: str = Field(description="The symbol of the quote currency.") + currency_name: str = Field(description="The name of the quote currency.") + base_currency_symbol: str = Field(description="The symbol of the base currency.") + base_currency_name: str = Field(description="The name of the base currency.") + last_updated_utc: datetime = Field(description="The last updated timestamp in UTC.") + delisted_utc: Optional[datetime] = Field( + description="The delisted timestamp in UTC." + ) + + @validator("last_updated_utc", pre=True, check_fields=False) + def last_updated_utc_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%dT%H:%M:%SZ") + + @validator("delisted_utc", pre=True, check_fields=False) + def delisted_utc_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%dT%H:%M:%SZ") + + +class PolygonForexPairsFetcher( + Fetcher[ + PolygonForexPairsQueryParams, + PolygonForexPairsData, + ] +): + @staticmethod + def transform_query(params: Dict[str, Any]) -> PolygonForexPairsQueryParams: + return PolygonForexPairsQueryParams(**params) + + @staticmethod + def extract_data( + query: PolygonForexPairsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> List[PolygonForexPairsData]: + api_key = credentials.get("polygon_api_key") if credentials else "" + + tickers = f"ticker=C:{query.symbol}" if query.symbol else "" + request_url = ( + f"https://api.polygon.io/v3/reference/" + f"tickers?{tickers}&market=fx&date={query.date}&" + f"search={query.search}&active={query.active}&order={query.order}&" + f"limit={query.limit}&sort={query.sort}&apiKey={api_key}" + ) + + data = {"next_url": request_url} + all_data: List[Dict] = [] + + while "next_url" in data: + data = get_data(request_url, **kwargs) + + if isinstance(data, list): + raise ValueError("Expected a dict, got a list") + + if len(data["results"]) == 0: + raise RuntimeError( + "No results found. Please change your query parameters." + ) + + if data["status"] == "OK": + all_data.extend(data["results"]) + elif data["status"] == "ERROR": + raise UserWarning(data["error"]) + + if "next_url" in data: + request_url = f"{data['next_url']}&apiKey={api_key}" + + return all_data + + @staticmethod + def transform_data( + data: List[PolygonForexPairsData], + ) -> List[PolygonForexPairsData]: + return data diff --git a/openbb_sdk/providers/polygon/openbb_polygon/models/forex_price.py b/openbb_sdk/providers/polygon/openbb_polygon/models/forex_price.py deleted file mode 100644 index fa69051cbaf..00000000000 --- a/openbb_sdk/providers/polygon/openbb_polygon/models/forex_price.py +++ /dev/null @@ -1,94 +0,0 @@ -"""Polygon Forex Price fetcher.""" - - -from datetime import datetime -from typing import Dict, List, Optional - -from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.forex_price import ForexPriceData, ForexPriceQueryParams -from pydantic import Field, NonNegativeFloat, PositiveFloat, PositiveInt - -from openbb_polygon.utils.helpers import get_data -from openbb_polygon.utils.types import BaseStockData, BaseStockQueryParams - - -class PolygonForexPriceQueryParams(BaseStockQueryParams): - """Polygon forex price query. - - Source: https://polygon.io/docs/forex/get_v2_aggs_ticker__forexticker__range__multiplier___timespan___from___to - - Parameters - ---------- - stocksTicker : str - The ticker symbol of the stocks to fetch. - start_date : Union[date, datetime] - The start date of the query. - end_date : Union[date, datetime] - The end date of the query. - timespan : Timespan, optional - The timespan of the query, by default Timespan.day - sort : Literal["asc", "desc"], optional - The sort order of the query, by default "desc" - limit : PositiveInt, optional - The limit of the query, by default 49999 - adjusted : bool, optional - Whether the query is adjusted, by default True - multiplier : PositiveInt, optional - The multiplier of the query, by default 1 - """ - - -class PolygonForexPriceData(BaseStockData): - v: NonNegativeFloat = Field(alias="volume") - n: PositiveInt - vw: Optional[PositiveFloat] - - -class PolygonForexPriceFetcher( - Fetcher[ - ForexPriceQueryParams, - ForexPriceData, - PolygonForexPriceQueryParams, - PolygonForexPriceData, - ] -): - @staticmethod - def transform_query( - query: ForexPriceQueryParams, extra_params: Optional[Dict] = None - ) -> PolygonForexPriceQueryParams: - return PolygonForexPriceQueryParams( - symbol=query.symbol, - start_date=query.start_date, - end_date=query.end_date if query.end_date else datetime.now(), - **extra_params if extra_params else {}, - ) - - @staticmethod - def extract_data( - query: PolygonForexPriceQueryParams, credentials: Optional[Dict[str, str]] - ) -> List[PolygonForexPriceData]: - if credentials: - api_key = credentials.get("polygon_api_key") - - request_url = ( - f"https://api.polygon.io/v2/aggs/ticker/" - f"C:{query.stocksTicker.upper()}/range/1/{query.timespan}/" - f"{query.start_date}/{query.end_date}?adjusted={query.adjusted}" - f"&sort={query.sort}&limit={query.limit}&multiplier={query.multiplier}" - f"&apiKey={api_key}" - ) - - data = get_data(request_url) - if isinstance(data, list): - raise ValueError("Expected a dict, got a list") - - if "results" not in data.keys() or len(data["results"]) == 0: - raise RuntimeError("No results found. Please change your query parameters.") - - data = data["results"] - return [PolygonForexPriceData(**d) for d in data] - - @staticmethod - def transform_data(data: List[PolygonForexPriceData]) -> List[ForexPriceData]: - return data_transformer(data, ForexPriceData) diff --git a/openbb_sdk/providers/polygon/openbb_polygon/models/income_statement.py b/openbb_sdk/providers/polygon/openbb_polygon/models/income_statement.py index 26850fdadbe..90070d67ed3 100644 --- a/openbb_sdk/providers/polygon/openbb_polygon/models/income_statement.py +++ b/openbb_sdk/providers/polygon/openbb_polygon/models/income_statement.py @@ -1,89 +1,109 @@ -from datetime import ( - date as dateType, - datetime, -) -from typing import Dict, List, Optional +from datetime import date as dateType +from typing import Any, Dict, List, Optional -from openbb_provider.abstract.data import Data from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer, get_querystring -from openbb_provider.models.income_statement import ( +from openbb_provider.standard_models.income_statement import ( IncomeStatementData, - IncomeStatementQueryParams, ) -from pydantic import Field +from openbb_provider.utils.helpers import get_querystring +from pydantic import validator from openbb_polygon.utils.helpers import get_data from openbb_polygon.utils.types import PolygonFundamentalQueryParams class PolygonIncomeStatementQueryParams(PolygonFundamentalQueryParams): - __doc__ = PolygonFundamentalQueryParams.__doc__ + """Polygon Income Statement Query Parameters""" -class PolygonIncomeStatementData(Data): - start_date: dateType = Field(alias="date") - tickers: Optional[List[str]] +class PolygonIncomeStatementData(IncomeStatementData): + class Config: + fields = { + "date": "start_date", + "accepted_date": "acceptance_datetime", + "period": "fiscal_period", + "revenue": "revenues", + "operating_income": "operating_income_loss", + "income_before_tax": "income_loss_from_continuing_operations_before_tax", + "income_tax_expense": "income_tax_expense_benefit", + "net_income": "net_income_loss", + "eps": "basic_earnings_per_share", + "eps_diluted": "diluted_earnings_per_share", + "interest_expense": "interest_expense_operating", + "symbol": "tickers", + } + + # tickers: Optional[List[str]] cik: Optional[str] filing_date: Optional[dateType] - acceptance_datetime: Optional[datetime] = Field(alias="accepted_date") - fiscal_period: Optional[str] = Field(alias="period") - revenues: Optional[float] = Field(alias="revenue") cost_of_revenue: Optional[float] gross_profit: Optional[float] operating_expenses: Optional[float] - income_loss_from_continuing_operations_before_tax: Optional[float] = Field( - alias="income_before_tax" - ) income_loss_from_continuing_operations_after_tax: Optional[float] - income_tax_expense_benefit: Optional[float] = Field(alias="income_tax_expense") - net_income_loss: Optional[float] = Field(alias="net_income") - basic_earnings_per_share: Optional[float] = Field(alias="eps") - diluted_earnings_per_share: Optional[float] = Field(alias="eps_diluted") benefits_costs_expenses: Optional[float] - interest_expense_operating: Optional[float] = Field(alias="interest_expense") net_income_loss_attributable_to_noncontrolling_interest: Optional[int] net_income_loss_attributable_to_parent: Optional[float] net_income_loss_available_to_common_stockholders_basic: Optional[float] - operating_income_loss: Optional[float] participating_securities_distributed_and_undistributed_earnings_loss_basic: Optional[ float ] preferred_stock_dividends_and_other_adjustments: Optional[float] + @validator("symbol", pre=True, check_fields=False) + def symbol_from_tickers(cls, v): # pylint: disable=E0213 + if isinstance(v, list): + return ",".join(v) + return v + class PolygonIncomeStatementFetcher( Fetcher[ - IncomeStatementQueryParams, - IncomeStatementData, PolygonIncomeStatementQueryParams, PolygonIncomeStatementData, ] ): @staticmethod - def transform_query( - query: IncomeStatementQueryParams, extra_params: Optional[Dict] = None - ) -> PolygonIncomeStatementQueryParams: - period = "annual" if query.period == "annually" else "quarterly" - return PolygonIncomeStatementQueryParams( - symbol=query.symbol, period=period, **extra_params if extra_params else {} # type: ignore - ) + def transform_query(params: Dict[str, Any]) -> PolygonIncomeStatementQueryParams: + return PolygonIncomeStatementQueryParams(**params) @staticmethod def extract_data( - query: PolygonIncomeStatementQueryParams, credentials: Optional[Dict[str, str]] + query: PolygonIncomeStatementQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[PolygonIncomeStatementData]: - if credentials: - api_key = credentials.get("polygon_api_key") + api_key = credentials.get("polygon_api_key") if credentials else "" + + query.period = "annual" if query.period == "annually" else "quarter" base_url = "https://api.polygon.io/vX/reference/financials" - query_string = get_querystring(query.dict(), []) + query_string = get_querystring(query.dict(by_alias=True), []) request_url = f"{base_url}?{query_string}&apiKey={api_key}" - data = get_data(request_url)["results"] + data = get_data(request_url, **kwargs)["results"] if len(data) == 0: raise RuntimeError("No Income Statement found") + FIELDS = [ + "revenues", + "cost_of_revenue", + "gross_profit", + "operating_expenses" "income_loss_from_continuing_operations_before_tax", + "income_tax_expense_benefit", + "net_income_loss", + "basic_earnings_per_share", + "diluted_earnings_per_share", + "net_income_loss_attributable_to_noncontrolling_interest", + "net_income_loss_attributable_to_parent", + "net_income_loss_available_to_common_stockholders_basic", + "operating_income_loss", + "participating_securities_distributed_and_undistributed_earnings_loss_basic", + "preferred_stock_dividends_and_other_adjustments", + "income_loss_from_continuing_operations_after_tax", + "benefits_costs_expenses", + "interest_expense_operating", + ] + to_return = [] for item in data: new = {"acceptance_datetime": item.get("acceptance_datetime")} @@ -92,53 +112,12 @@ class PolygonIncomeStatementFetcher( new["fiscal_period"] = item["fiscal_period"] new["tickers"] = item["tickers"] new["cik"] = item["cik"] - incs = item["financials"]["income_statement"] - new["revenues"] = incs["revenues"].get("value") - new["cost_of_revenue"] = incs.get("cost_of_revenue", {}).get("value", 0) - new["gross_profit"] = incs.get("gross_profit", {}).get("value", 0) - new["operating_expenses"] = incs["operating_expenses"].get("value") - new["income_loss_from_continuing_operations_before_tax"] = incs[ - "income_loss_from_continuing_operations_before_tax" - ].get("value") - new["income_tax_expense_benefit"] = incs["income_tax_expense_benefit"].get( - "value" - ) - new["net_income_loss"] = incs["net_income_loss"].get("value") - new["basic_earnings_per_share"] = incs.get( - "basic_earnings_per_share", {} - ).get("value") - new["diluted_earnings_per_share"] = incs.get( - "diluted_earnings_per_share", {} - ).get("value") - new["net_income_loss_attributable_to_noncontrolling_interest"] = incs[ - "net_income_loss_attributable_to_noncontrolling_interest" - ].get("value") - new["net_income_loss_attributable_to_parent"] = incs[ - "net_income_loss_attributable_to_parent" - ].get("value") - new["net_income_loss_available_to_common_stockholders_basic"] = incs[ - "net_income_loss_available_to_common_stockholders_basic" - ].get("value") - new["operating_income_loss"] = incs["operating_income_loss"].get("value") - new[ - "participating_securities_distributed_and_undistributed_earnings_loss_basic" - ] = incs[ - "participating_securities_distributed_and_undistributed_earnings_loss_basic" - ].get( - "value" - ) - new["preferred_stock_dividends_and_other_adjustments"] = incs[ - "preferred_stock_dividends_and_other_adjustments" - ].get("value") - new["income_loss_from_continuing_operations_after_tax"] = incs[ - "income_loss_from_continuing_operations_after_tax" - ].get("value") - new["benefits_costs_expenses"] = incs["benefits_costs_expenses"].get( - "value" - ) - new["interest_expense_operating"] = incs.get( - "interest_expense_operating", {} - ).get("value", 0) + incs = item["financials"].get("income_statement", {}) + + if incs: + for field in FIELDS: + new[field] = incs.get(field, {}).get("value", 0) + to_return.append(PolygonIncomeStatementData(**new)) return to_return @@ -146,5 +125,4 @@ class PolygonIncomeStatementFetcher( def transform_data( data: List[PolygonIncomeStatementData], ) -> List[IncomeStatementData]: - processors = {"tickers": lambda x: "" if not x else ",".join(x)} - return data_transformer(data, IncomeStatementData, processors) + return data diff --git a/openbb_sdk/providers/polygon/openbb_polygon/models/major_indices_eod.py b/openbb_sdk/providers/polygon/openbb_polygon/models/major_indices_eod.py index b5faf53da3e..191fbf4994d 100644 --- a/openbb_sdk/providers/polygon/openbb_polygon/models/major_indices_eod.py +++ b/openbb_sdk/providers/polygon/openbb_polygon/models/major_indices_eod.py @@ -1,89 +1,104 @@ """Polygon major indices end of day fetcher.""" -from datetime import datetime -from typing import Dict, List, Optional +from datetime import datetime, timedelta +from typing import Any, Dict, List, Literal, Optional from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.major_indices_eod import ( +from openbb_provider.standard_models.major_indices_eod import ( MajorIndicesEODData, MajorIndicesEODQueryParams, ) +from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS +from pydantic import Field, PositiveFloat, PositiveInt, validator from openbb_polygon.utils.helpers import get_data -from openbb_polygon.utils.types import BaseStockData, BaseStockQueryParams -class PolygonMajorIndicesEODQueryParams(BaseStockQueryParams): - """Polygon major indices end of day query. +class PolygonMajorIndicesEODQueryParams(MajorIndicesEODQueryParams): + """Polygon major indices end of day Query. Source: https://polygon.io/docs/indices/getting-started - - Parameters - ---------- - symbol : str - The symbol of the stocks to fetch. - start_date : Union[date, datetime] - The start date of the query. - end_date : Union[date, datetime] - The end date of the query. - timespan : Timespan, optional - The timespan of the query, by default Timespan.day - sort : Literal["asc", "desc"], optional - The sort order of the query, by default "desc" - limit : PositiveInt, optional - The limit of the query, by default 49999 - adjusted : bool, optional - Whether the query is adjusted, by default True - multiplier : PositiveInt, optional - The multiplier of the query, by default 1 """ + timespan: Literal[ + "minute", "hour", "day", "week", "month", "quarter", "year" + ] = Field(default="day", description="The timespan of the data.") + sort: Literal["asc", "desc"] = Field( + default="desc", description="Sort order of the data." + ) + limit: PositiveInt = Field( + default=49999, description=QUERY_DESCRIPTIONS.get("limit", "") + ) + adjusted: bool = Field(default=True, description="Whether the data is adjusted.") + multiplier: PositiveInt = Field( + default=1, description="The multiplier of the timespan." + ) -class PolygonMajorIndicesEODData(BaseStockData): - """Polygon major indices end of day data.""" + +class PolygonMajorIndicesEODData(MajorIndicesEODData): + """Polygon major indices end of day Data.""" + + class Config: + fields = { + "open": "o", + "high": "h", + "low": "l", + "close": "c", + "volume": "v", + } + + t: datetime = Field(description="The timestamp of the data.") + n: PositiveInt = Field( + description="The number of transactions for the symbol in the time period." + ) + vw: PositiveFloat = Field( + description="The volume weighted average price of the symbol." + ) + + @validator("t", pre=True, check_fields=False) + def time_validate(cls, v): # pylint: disable=E0213 + return datetime.fromtimestamp(v / 1000) class PolygonMajorIndicesEODFetcher( Fetcher[ - MajorIndicesEODQueryParams, - MajorIndicesEODData, PolygonMajorIndicesEODQueryParams, PolygonMajorIndicesEODData, ] ): @staticmethod - def transform_query( - query: MajorIndicesEODQueryParams, extra_params: Optional[Dict] = None - ) -> PolygonMajorIndicesEODQueryParams: - return PolygonMajorIndicesEODQueryParams( - symbol=query.symbol, - start_date=query.start_date, - end_date=query.end_date if query.end_date else datetime.now(), - **extra_params if extra_params else {}, - ) + def transform_query(params: Dict[str, Any]) -> PolygonMajorIndicesEODQueryParams: + now = datetime.now().date() + transformed_params = params + if params.get("start_date") is None: + transformed_params["start_date"] = now - timedelta(days=7) + + if params.get("end_date") is None: + transformed_params["end_date"] = now + return PolygonMajorIndicesEODQueryParams(**transformed_params) @staticmethod def extract_data( - query: PolygonMajorIndicesEODQueryParams, credentials: Optional[Dict[str, str]] + query: PolygonMajorIndicesEODQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[PolygonMajorIndicesEODData]: - if credentials: - api_key = credentials.get("polygon_api_key") + api_key = credentials.get("polygon_api_key") if credentials else "" request_url = ( f"https://api.polygon.io/v2/aggs/ticker/" - f"I:{query.stocksTicker.upper()}/range/1/{query.timespan}/" + f"I:{query.symbol}/range/1/{query.timespan}/" f"{query.start_date}/{query.end_date}?adjusted={query.adjusted}" f"&sort={query.sort}&limit={query.limit}&multiplier={query.multiplier}" f"&apiKey={api_key}" ) - data = get_data(request_url) + data = get_data(request_url, **kwargs) if isinstance(data, list): raise ValueError("Expected a dict, got a list") - if "results" not in data.keys() or len(data["results"]) == 0: + if "results" not in data or len(data["results"]) == 0: raise RuntimeError("No results found. Please change your query parameters.") data = data["results"] @@ -92,5 +107,5 @@ class PolygonMajorIndicesEODFetcher( @staticmethod def transform_data( data: List[PolygonMajorIndicesEODData], - ) -> List[MajorIndicesEODData]: - return data_transformer(data, MajorIndicesEODData) + ) -> List[PolygonMajorIndicesEODData]: + return data diff --git a/openbb_sdk/providers/polygon/openbb_polygon/models/major_indices_price.py b/openbb_sdk/providers/polygon/openbb_polygon/models/major_indices_price.py deleted file mode 100644 index 9c0c5a8269c..00000000000 --- a/openbb_sdk/providers/polygon/openbb_polygon/models/major_indices_price.py +++ /dev/null @@ -1,94 +0,0 @@ -"""Polygon major indices price fetcher.""" - - -from typing import Dict, List, Optional - -from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.major_indices_price import ( - MajorIndicesPriceData, - MajorIndicesPriceQueryParams, -) - -from openbb_polygon.utils.helpers import get_data -from openbb_polygon.utils.types import BaseStockData, BaseStockQueryParams - - -class PolygonMajorIndicesPriceQueryParams(BaseStockQueryParams): - """Polygon major indices price query. - - Source: https://polygon.io/docs/indices/getting-started - - Parameters - ---------- - symbol : str - The symbol of the index. - start_date : Union[date, datetime] - The start date of the query. - end_date : Union[date, datetime] - The end date of the query. - timespan : Timespan, optional - The timespan of the query, by default Timespan.day - sort : Literal["asc", "desc"], optional - The sort order of the query, by default "desc" - limit : PositiveInt, optional - The limit of the query, by default 49999 - adjusted : bool, optional - Whether the query is adjusted, by default True - multiplier : PositiveInt, optional - The multiplier of the query, by default 1 - """ - - -class PolygonMajorIndicesPriceData(BaseStockData): - """Polygon major indices price data.""" - - -class PolygonMajorIndicesPriceFetcher( - Fetcher[ - MajorIndicesPriceQueryParams, - MajorIndicesPriceData, - PolygonMajorIndicesPriceQueryParams, - PolygonMajorIndicesPriceData, - ] -): - @staticmethod - def transform_query( - query: MajorIndicesPriceQueryParams, extra_params: Optional[Dict] = None - ) -> PolygonMajorIndicesPriceQueryParams: - return PolygonMajorIndicesPriceQueryParams( - symbol=query.symbol, - **extra_params or {}, - ) - - @staticmethod - def extract_data( - query: PolygonMajorIndicesPriceQueryParams, - credentials: Optional[Dict[str, str]], - ) -> List[PolygonMajorIndicesPriceData]: - if credentials: - api_key = credentials.get("polygon_api_key") - - request_url = ( - f"https://api.polygon.io/v2/aggs/ticker/" - f"I:{query.stocksTicker.upper()}/range/1/{query.timespan}/" - f"{query.start_date}/{query.end_date}?adjusted={query.adjusted}" - f"&sort={query.sort}&limit={query.limit}&multiplier={query.multiplier}" - f"&apiKey={api_key}" - ) - - data = get_data(request_url) - if isinstance(data, list): - raise ValueError("Expected a dict, got a list") - - if "results" not in data.keys() or len(data["results"]) == 0: - raise RuntimeError("No results found. Please change your query parameters.") - - data = data["results"] - return [PolygonMajorIndicesPriceData(**d) for d in data] - - @staticmethod - def transform_data( - data: List[PolygonMajorIndicesPriceData], - ) -> List[MajorIndicesPriceData]: - return data_transformer(data, MajorIndicesPriceData) diff --git a/openbb_sdk/providers/polygon/openbb_polygon/models/stock_eod.py b/openbb_sdk/providers/polygon/openbb_polygon/models/stock_eod.py index 9fae5c614d4..73a0f6dac43 100644 --- a/openbb_sdk/providers/polygon/openbb_polygon/models/stock_eod.py +++ b/openbb_sdk/providers/polygon/openbb_polygon/models/stock_eod.py @@ -2,96 +2,103 @@ from datetime import datetime, timedelta -from typing import Dict, List, Optional +from typing import Any, Dict, List, Literal, Optional from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.stock_eod import StockEODData, StockEODQueryParams -from pydantic import Field, NonNegativeFloat, PositiveFloat, PositiveInt +from openbb_provider.standard_models.stock_eod import StockEODData, StockEODQueryParams +from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS +from pydantic import Field, PositiveInt, validator from openbb_polygon.utils.helpers import get_data -from openbb_polygon.utils.types import BaseStockData, BaseStockQueryParams -class PolygonStockEODQueryParams(BaseStockQueryParams): - """Polygon stocks end of day query. +class PolygonStockEODQueryParams(StockEODQueryParams): + """Polygon stocks end of day Query. Source: https://polygon.io/docs/stocks/getting-started - - Parameters - ---------- - symbol : str - The symbol of the stocks to fetch. - start_date : Union[date, datetime] - The start date of the query. - end_date : Union[date, datetime] - The end date of the query. - timespan : Timespan, optional - The timespan of the query, by default Timespan.day - sort : Literal["asc", "desc"], optional - The sort order of the query, by default "desc" - limit : PositiveInt, optional - The limit of the query, by default 49999 - adjusted : bool, optional - Whether the query is adjusted, by default True - multiplier : PositiveInt, optional - The multiplier of the query, by default 1 """ + timespan: Literal[ + "minute", "hour", "day", "week", "month", "quarter", "year" + ] = Field(default="day", description="The timespan of the data.") + sort: Literal["asc", "desc"] = Field( + default="desc", description="Sort order of the data." + ) + limit: PositiveInt = Field( + default=49999, description=QUERY_DESCRIPTIONS.get("limit", "") + ) + adjusted: bool = Field(default=True, description="Whether the data is adjusted.") + multiplier: PositiveInt = Field( + default=1, description="The multiplier of the timespan." + ) -class PolygonStockEODData(BaseStockData): - v: NonNegativeFloat = Field(alias="volume") - n: PositiveInt - vw: Optional[PositiveFloat] + +class PolygonStockEODData(StockEODData): + """Polygon stocks end of day Data.""" + + class Config: + fields = { + "date": "t", + "open": "o", + "high": "h", + "low": "l", + "close": "c", + "volume": "v", + "vwap": "vw", + } + + n: PositiveInt = Field( + description="The number of transactions for the symbol in the time period." + ) + + @validator("t", pre=True, check_fields=False) + def t_validate(cls, v): # pylint: disable=E0213 + return datetime.fromtimestamp(v / 1000) class PolygonStockEODFetcher( Fetcher[ - StockEODQueryParams, - StockEODData, PolygonStockEODQueryParams, PolygonStockEODData, ] ): @staticmethod - def transform_query( - query: StockEODQueryParams, extra_params: Optional[Dict] = None - ) -> PolygonStockEODQueryParams: - now = datetime.now() - start_date = query.start_date or (now - timedelta(days=5)).date() - end_date = query.end_date or now.date() - return PolygonStockEODQueryParams( - symbol=query.symbol, - start_date=start_date, - end_date=end_date, - **extra_params or {}, - ) + def transform_query(params: Dict[str, Any]) -> PolygonStockEODQueryParams: + now = datetime.now().date() + transformed_params = params + if params.get("start_date") is None: + transformed_params["start_date"] = now - timedelta(days=7) + + if params.get("end_date") is None: + transformed_params["end_date"] = now + return PolygonStockEODQueryParams(**transformed_params) @staticmethod def extract_data( - query: PolygonStockEODQueryParams, credentials: Optional[Dict[str, str]] + query: PolygonStockEODQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[PolygonStockEODData]: - if credentials: - api_key = credentials.get("polygon_api_key") + api_key = credentials.get("polygon_api_key") if credentials else "" request_url = ( f"https://api.polygon.io/v2/aggs/ticker/" - f"{query.stocksTicker.upper()}/range/1/{str(query.timespan.value)}/" + f"{query.symbol}/range/1/{str(query.timespan)}/" f"{query.start_date}/{query.end_date}?adjusted={query.adjusted}" f"&sort={query.sort}&limit={query.limit}&multiplier={query.multiplier}" f"&apiKey={api_key}" ) - data = get_data(request_url) + data = get_data(request_url, **kwargs) if isinstance(data, list): raise ValueError("Expected a dict, got a list") - if "results" not in data.keys() or len(data["results"]) == 0: + if "results" not in data or len(data["results"]) == 0: raise RuntimeError("No results found. Please change your query parameters.") data = data["results"] return [PolygonStockEODData(**d) for d in data] @staticmethod - def transform_data(data: List[PolygonStockEODData]) -> List[StockEODData]: - return data_transformer(data, StockEODData) + def transform_data(data: List[PolygonStockEODData]) -> List[PolygonStockEODData]: + return data diff --git a/openbb_sdk/providers/polygon/openbb_polygon/models/stock_news.py b/openbb_sdk/providers/polygon/openbb_polygon/models/stock_news.py index 2d27afa2a57..dd3db9a50be 100644 --- a/openbb_sdk/providers/polygon/openbb_polygon/models/stock_news.py +++ b/openbb_sdk/providers/polygon/openbb_polygon/models/stock_news.py @@ -1,118 +1,111 @@ """Polygon Stock News Fetcher.""" -from datetime import datetime -from typing import Dict, List, Literal, Optional +from typing import Any, Dict, List, Literal, Optional -from openbb_provider.abstract.data import Data, QueryParams from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer, get_querystring -from openbb_provider.models.stock_news import StockNewsData, StockNewsQueryParams +from openbb_provider.standard_models.stock_news import ( + StockNewsData, + StockNewsQueryParams, +) +from openbb_provider.utils.helpers import data_transformer, get_querystring from pydantic import BaseModel, Field from openbb_polygon.utils.helpers import get_data -class PolygonStockNewsQueryParams(QueryParams): +class PolygonStockNewsQueryParams(StockNewsQueryParams): """Polygon stock news query. Source: https://polygon.io/docs/stocks/get_v2_reference_news - - Parameters - ---------- - symbol : str - The symbol of the stocks to fetch. - ticker_lt : str, optional - Less than, by default None - ticker_lte : str, optional - Less than or equal, by default None - ticker_gt : str, optional - Greater than, by default None - ticker_gte : str, optional - Greater than or equal, by default None - published_utc : str, optional - The published date of the query, by default None - published_utc_lt : str, optional - Less than, by default None - published_utc_lte : str, optional - Less than or equal, by default None - published_utc_gt : str, optional - Greater than, by default None - published_utc_gte : str, optional - Greater than or equal, by default None - order : Literal["asc", "desc"], optional - The sort order of the query, by default None - limit : int, optional - The limit of the query, by default 100 - sort : str, optional - The sort of the query, by default None """ - ticker: str = Field(alias="symbols") - ticker_lt: Optional[str] = Field(alias="ticker.lt", default=None) - ticker_lte: Optional[str] = Field(alias="ticker.lte", default=None) - ticker_gt: Optional[str] = Field(alias="ticker.gt", default=None) - ticker_gte: Optional[str] = Field(alias="ticker.gte", default=None) - published_utc: Optional[str] = None - published_utc_lt: Optional[str] = Field(alias="published_utc.lt", default=None) - published_utc_lte: Optional[str] = Field(alias="published_utc.lte", default=None) - published_utc_gt: Optional[str] = Field(alias="published_utc.gt", default=None) - published_utc_gte: Optional[str] = Field(alias="published_utc.gte", default=None) - order: Optional[Literal["asc", "desc"]] = None - limit: Optional[int] = Field(default=100) - sort: Optional[str] = None + class Config: + fields = {"symbols": "ticker"} + + ticker_lt: Optional[str] = Field( + default=None, description="Less than, by default None" + ) + ticker_lte: Optional[str] = Field( + default=None, description="Less than or equal, by default None" + ) + ticker_gt: Optional[str] = Field( + default=None, description="Greater than, by default None" + ) + ticker_gte: Optional[str] = Field( + default=None, description="Greater than or equal, by default None" + ) + published_utc: Optional[str] = Field( + default=None, description="The published date of the query, by default None" + ) + published_utc_lt: Optional[str] = Field( + default=None, description="Less than, by default None" + ) + published_utc_lte: Optional[str] = Field( + default=None, description="Less than or equal, by default None" + ) + published_utc_gt: Optional[str] = Field( + default=None, description="Greater than, by default None" + ) + published_utc_gte: Optional[str] = Field( + default=None, description="Greater than or equal, by default None" + ) + order: Optional[Literal["asc", "desc"]] = Field( + default=None, description="The sort order of the query, by default None" + ) + sort: Optional[str] = Field( + default=None, description="The sort of the query, by default None" + ) class PolygonPublisher(BaseModel): - favicon_url: str - homepage_url: str - logo_url: str - name: str + favicon_url: str = Field(description="Favicon URL.") + homepage_url: str = Field(description="Homepage URL.") + logo_url: str = Field(description="Logo URL.") + name: str = Field(description="Publisher Name.") -class PolygonStockNewsData(Data): +class PolygonStockNewsData(StockNewsData): """Source: https://polygon.io/docs/stocks/get_v2_reference_news""" - amp_url: Optional[str] - article_url: str = Field(alias="url") - author: Optional[str] - description: Optional[str] = Field(alias="text") - id: str - image_url: Optional[str] - keywords: Optional[List[str]] - published_utc: datetime = Field(alias="date") - publisher: PolygonPublisher = Field(alias="image") - tickers: List[str] - title: str + class Config: + fields = { + "url": "article_url", + "text": "description", + "date": "published_utc", + } + + amp_url: Optional[str] = Field(description="AMP URL.") + author: Optional[str] = Field(description="Author of the article.") + id: str = Field(description="Article ID.") + image_url: Optional[str] = Field(description="Image URL.") + keywords: Optional[List[str]] = Field(description="Keywords in the article") + publisher: PolygonPublisher = Field(description="Publisher of the article.") + tickers: List[str] = Field(description="Tickers covered in the article.") class PolygonStockNewsFetcher( Fetcher[ - StockNewsQueryParams, - StockNewsData, PolygonStockNewsQueryParams, PolygonStockNewsData, ] ): @staticmethod - def transform_query( - query: StockNewsQueryParams, extra_params: Optional[Dict] = None - ) -> PolygonStockNewsQueryParams: - return PolygonStockNewsQueryParams( - symbols=query.symbols, **extra_params if extra_params else {} - ) + def transform_query(params: Dict[str, Any]) -> PolygonStockNewsQueryParams: + return PolygonStockNewsQueryParams(**params) @staticmethod def extract_data( - query: PolygonStockNewsQueryParams, credentials: Optional[Dict[str, str]] + query: PolygonStockNewsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, ) -> List[PolygonStockNewsData]: - if credentials: - api_key = credentials.get("polygon_api_key") + api_key = credentials.get("polygon_api_key") if credentials else "" base_url = "https://api.polygon.io/v2/reference/news" - querystring = get_querystring(query.dict(), []) + querystring = get_querystring(query.dict(by_alias=True), []) request_url = f"{base_url}?{querystring}&apiKey={api_key}" - data = get_data(request_url)["results"] + data = get_data(request_url, **kwargs)["results"] if len(data) == 0: raise RuntimeError("No news found") diff --git a/openbb_sdk/providers/polygon/openbb_polygon/models/stock_price.py b/openbb_sdk/providers/polygon/openbb_polygon/models/stock_price.py deleted file mode 100644 index 083ab8338af..00000000000 --- a/openbb_sdk/providers/polygon/openbb_polygon/models/stock_price.py +++ /dev/null @@ -1,94 +0,0 @@ -"""Polygon stocks price fetcher.""" - - -from datetime import datetime -from typing import Dict, List, Optional - -from openbb_provider.abstract.fetcher import Fetcher -from openbb_provider.helpers import data_transformer -from openbb_provider.models.stock_price import StockPriceData, StockPriceQueryParams -from pydantic import Field, NonNegativeFloat, PositiveFloat, PositiveInt - -from openbb_polygon.utils.helpers import get_data -from openbb_polygon.utils.types import BaseStockData, BaseStockQueryParams - - -class PolygonStockPriceQueryParams(BaseStockQueryParams): - """Polygon stocks price query. - - Source: https://polygon.io/docs/stocks/getting-started - - Parameters - ---------- - symbol : str - The symbol of the stocks to fetch. - start_date : Union[date, datetime] - The start date of the query. - end_date : Union[date, datetime] - The end date of the query. - timespan : Timespan, optional - The timespan of the query, by default Timespan.day - sort : Literal["asc", "desc"], optional - The sort order of the query, by default "desc" - limit : PositiveInt, optional - The limit of the query, by default 49999 - adjusted : bool, optional - Whether the query is adjusted, by default True - multiplier : PositiveInt, optional - The multiplier of the query, by default 1 - """ - - -class PolygonStockPriceData(BaseStockData): - v: NonNegativeFloat = Field(alias="volume") - n: PositiveInt - vw: PositiveFloat - - -class PolygonStockPriceFetcher( - Fetcher[ - StockPriceQueryParams, - StockPriceData, - PolygonStockPriceQueryParams, - PolygonStockPriceData, - ] -): - @staticmethod - def transform_query( - query: StockPriceQueryParams, extra_params: Optional[Dict] = None - ) -> PolygonStockPriceQueryParams: - return PolygonStockPriceQueryParams( - symbol=query.symbol, - start_date=query.start_date, - end_date=query.end_date if query.end_date else datetime.now(), - **extra_params if extra_params else {}, - ) - - @staticmethod - def extract_data( - query: PolygonStockPriceQueryParams, credentials: Optional[Dict[str, str]] - ) -> List[PolygonStockPriceData]: - if credentials: - api_key = credentials.get("polygon_api_key") - - request_url = ( - f"https://api.polygon.io/v2/aggs/ticker/" - f"{query.stocksTicker.upper()}/range/1/{query.timespan}/" - f"{query.start_date}/{query.end_date}?adjusted={query.adjusted}" - f"&sort={query.sort}&limit={query.limit}&multiplier={query.multiplier}" - f"&apiKey={api_key}" - ) - - data = get_data(request_url) - if isinstance(data, list): - raise ValueError("Expected a dict, got a list") - - if "results" not in data.keys() or len(data["results"]) == 0: - raise RuntimeError("No results found. Please change your query parameters.") - - data = data["results"] - return [PolygonStockPriceData(**d) for d in data] - - @staticmethod - def transform_data(data: List[PolygonStockPriceData]) -> List[StockPriceData]: - return data_transformer(data, StockPriceData) diff --git a/openbb_sdk/providers/polygon/openbb_polygon/provider.py b/openbb_sdk/providers/polygon/openbb_polygon/provider.py deleted file mode 100644 index dabee82dc54..00000000000 --- a/openbb_sdk/providers/polygon/openbb_polygon/provider.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Polygon provider module.""" - - -from openbb_provider.abstract.provider import Provider - -from openbb_polygon.models.balance_sheet import PolygonBalanceSheetFetcher -from openbb_polygon.models.cash_flow import PolygonCashFlowStatementFetcher -from openbb_polygon.models.crypto_eod import PolygonCryptoEODFetcher -from openbb_polygon.models.crypto_price import PolygonCryptoPriceFetcher -from openbb_polygon.models.forex_eod import PolygonForexEODFetcher -from openbb_polygon.models.forex_price import PolygonForexPriceFetcher -from openbb_polygon.models.income_statement import PolygonIncomeStatementFetcher -from openbb_polygon.models.major_indices_eod import PolygonMajorIndicesEODFetcher -from openbb_polygon.models.major_indices_price import PolygonMajorIndicesPriceFetcher -from openbb_polygon.models.stock_eod import PolygonStockEODFetcher -from openbb_polygon.models.stock_news import PolygonStockNewsFetcher -from openbb_polygon.models.stock_price import PolygonStockPriceFetcher - -# mypy: disable-error-code="list-item" -polygon_provider = Provider( - name="polygon", - description="Provider for Polygon.", - required_credentials=["api_key"], - fetcher_list=[ - PolygonStockEODFetcher, - PolygonStockNewsFetcher, - PolygonBalanceSheetFetcher, - PolygonIncomeStatementFetcher, - PolygonCashFlowStatementFetcher, - PolygonStockPriceFetcher, - PolygonCryptoPriceFetcher, - PolygonCryptoEODFetcher, - PolygonMajorIndicesEODFetcher, - PolygonMajorIndicesPriceFetcher, - PolygonForexEODFetcher, - PolygonForexPriceFetcher, - ], -) diff --git a/openbb_sdk/providers/polygon/openbb_polygon/utils/helpers.py b/openbb_sdk/providers/polygon/openbb_polygon/utils/helpers.py index 3dd68dad73e..a2b65faaf99 100644 --- a/openbb_sdk/providers/polygon/openbb_polygon/utils/helpers.py +++ b/openbb_sdk/providers/polygon/openbb_polygon/utils/helpers.py @@ -1,9 +1,9 @@ # IMPORT THIRD PARTY -import requests +from openbb_provider import helpers -def get_data(url: str) -> dict: - r = requests.get(url, timeout=10) +def get_data(url: str, **kwargs) -> dict: + r = helpers.make_request(url, **kwargs) if r.status_code != 200: data = r.json() message = data.get("message") diff --git a/openbb_sdk/providers/polygon/openbb_polygon/utils/types.py b/openbb_sdk/providers/polygon/openbb_polygon/utils/types.py index fe123d35c25..2e1a247ec0d 100644 --- a/openbb_sdk/providers/polygon/openbb_polygon/utils/types.py +++ b/openbb_sdk/providers/polygon/openbb_polygon/utils/types.py @@ -1,117 +1,68 @@ """Polygon types helpers.""" -from datetime import date, datetime, timedelta -from enum import Enum -from typing import Literal, Optional, Union +from datetime import date +from typing import Literal, Optional -from openbb_provider.abstract.data import Data, QueryParams -from pydantic import Field, PositiveFloat, PositiveInt, validator +from openbb_provider.standard_models.income_statement import IncomeStatementQueryParams +from pydantic import Field -class Timespan(str, Enum): - minute = "minute" - hour = "hour" - day = "day" - week = "week" - month = "month" - quarter = "quarter" - year = "year" - - -class PolygonFundamentalQueryParams(QueryParams): +class PolygonFundamentalQueryParams(IncomeStatementQueryParams): """Polygon Fundamental QueryParams. Source: https://polygon.io/docs/stocks#!/get_vx_reference_financials - - Parameter - --------- - symbol : Optional[str] - The symbol of the company if no CIK is provided. - cik : Optional[str] - The CIK of the company if no symbol is provided. - company_name : Optional[str] - The name of the company. - company_name_search : Optional[str] - The name of the company to search. - sic : Optional[str] - The Standard Industrial Classification (SIC) of the company. - filing_date : Optional[date] - The filing date of the financial statement. - filing_date_lt : Optional[date] - The filing date less than the given date. - filing_date_lte : Optional[date] - The filing date less than or equal to the given date. - filing_date_gt : Optional[date] - The filing date greater than the given date. - filing_date_gte : Optional[date] - The filing date greater than or equal to the given date. - period_of_report_date : Optional[date] - The period of report date of the financial statement. - period_of_report_date_lt : Optional[date] - The period of report date less than the given date. - period_of_report_date_lte : Optional[date] - The period of report date less than or equal to the given date. - period_of_report_date_gt : Optional[date] - The period of report date greater than the given date. - period_of_report_date_gte : Optional[date] - The period of report date greater than or equal to the given date. - timeframe : Optional[Literal["annual", "quarterly", "ttm"]] - The timeframe of the financial statement. - include_sources : Optional[bool] - Whether to include the sources of the financial statement. - order : Optional[Literal["asc", "desc"]] - The order of the financial statement. - limit : Optional[PositiveInt] - The limit of the financial statement. - sort : Optional[Literal["filing_date", "period_of_report_date"]] - The sort of the financial statement. """ - ticker: Optional[str] = Field(alias="symbol") - cik: Optional[str] - company_name: Optional[str] - company_name_search: Optional[str] = Field(alias="company_name.search") - sic: Optional[str] - filing_date: Optional[date] - filing_date_lt: Optional[date] = Field(alias="filing_date.lt") - filing_date_lte: Optional[date] = Field(alias="filing_date.lte") - filing_date_gt: Optional[date] = Field(alias="filing_date.gt") - filing_date_gte: Optional[date] = Field(alias="filing_date.gte") - period_of_report_date: Optional[date] - period_of_report_date_lt: Optional[date] = Field(alias="period_of_report_date.lt") - period_of_report_date_lte: Optional[date] = Field(alias="period_of_report_date.lte") - period_of_report_date_gt: Optional[date] = Field(alias="period_of_report_date.gt") - period_of_report_date_gte: Optional[date] = Field(alias="period_of_report_date.gte") - timeframe: Optional[Literal["annual", "quarterly", "ttm"]] = Field(alias="period") - include_sources: Optional[bool] - order: Optional[Literal["asc", "desc"]] - limit: Optional[PositiveInt] = 100 - sort: Optional[Literal["filing_date", "period_of_report_date"]] + class Config: + fields = { + "symbol": "ticker", + "period": "timeframe", + } - -class BaseStockQueryParams(QueryParams): - stocksTicker: str = Field(alias="symbol") - start_date: Union[date, datetime] = Field( - default=datetime.now().date() - timedelta(days=8) + company_name: Optional[str] = Field(description="The name of the company.") + company_name_search: Optional[str] = Field( + description="The name of the company to search." ) - end_date: Union[date, datetime] = Field( - default=datetime.now().date() - timedelta(days=1) + sic: Optional[str] = Field( + description="The Standard Industrial Classification (SIC) of the company." + ) + filing_date: Optional[date] = Field( + description="The filing date of the financial statement." + ) + filing_date_lt: Optional[date] = Field( + description="The filing date less than the given date." + ) + filing_date_lte: Optional[date] = Field( + description="The filing date less than or equal to the given date.", + ) + filing_date_gt: Optional[date] = Field( + description="The filing date greater than the given date.", + ) + filing_date_gte: Optional[date] = Field( + description="The filing date greater than or equal to the given date.", + ) + period_of_report_date: Optional[date] = Field( + description="The period of report date of the financial statement." + ) + period_of_report_date_lt: Optional[date] = Field( + description="The period of report date less than the given date.", + ) + period_of_report_date_lte: Optional[date] = Field( + description="The period of report date less than or equal to the given date.", + ) + period_of_report_date_gt: Optional[date] = Field( + description="The period of report date greater than the given date.", + ) + period_of_report_date_gte: Optional[date] = Field( + description="The period of report date greater than or equal to the given date.", + ) + include_sources: Optional[bool] = Field( + description="Whether to include the sources of the financial statement." + ) + order: Optional[Literal["asc", "desc"]] = Field( + description="The order of the financial statement." + ) + sort: Optional[Literal["filing_date", "period_of_report_date"]] = Field( + description="The sort of the financial statement." ) - timespan: Timespan = Field(default=Timespan.day) - sort: Literal["asc", "desc"] = Field(default="desc") - limit: PositiveInt = Field(default=49999) - adjusted: bool = Field(default=True) - multiplier: PositiveInt = Field(default=1) - - -class BaseStockData(Data): - c: PositiveFloat = Field(alias="close") - o: PositiveFloat = Field(alias="open") - h: PositiveFloat = Field(alias="high") - l: PositiveFloat = Field(alias="low") # noqa: E741 - t: datetime = Field(alias="date") - - @validator("t", pre=True) - def time_validate(cls, v): # pylint: disable=E0213 - return datetime.fromtimestamp(v / 1000) diff --git a/openbb_sdk/providers/polygon/pyproject.toml b/openbb_sdk/providers/polygon/pyproject.toml index d4f3ed18b94..2ad267bbf89 100644 --- a/openbb_sdk/providers/polygon/pyproject.toml +++ b/openbb_sdk/providers/polygon/pyproject.toml @@ -15,4 +15,4 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.poetry.plugins."openbb_provider_extension"] -polygon = "openbb_polygon.provider:polygon_provider" +polygon = "openbb_polygon:polygon_provider" diff --git a/openbb_sdk/providers/polygon/tests/.gitkeep b/openbb_sdk/providers/polygon/tests/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/pyproject.toml b/openbb_sdk/pyproject.toml index 13487a2e048..15f9bdd8616 100644 --- a/openbb_sdk/pyproject.toml +++ b/openbb_sdk/pyproject.toml @@ -22,6 +22,10 @@ openbb-fmp = { path = "./providers/fmp" } openbb-polygon = { path = "./providers/polygon" } openbb-fred = { path = "./providers/fred" } + +[tool.poetry.group.dev.dependencies] +pytest = "^6.2.2" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/openbb_sdk/sdk/core/openbb_core/api/dependency/system.py b/openbb_sdk/sdk/core/openbb_core/api/dependency/system.py index b908e3ba490..4ccc1c5e0b4 100644 --- a/openbb_sdk/sdk/core/openbb_core/api/dependency/system.py +++ b/openbb_sdk/sdk/core/openbb_core/api/dependency/system.py @@ -1,3 +1,4 @@ +"""System dependency.""" from copy import deepcopy from openbb_core.app.model.system_settings import SystemSettings @@ -7,10 +8,12 @@ __system_service = SystemService() async def get_system_settings() -> SystemSettings: + """Get system settings.""" global __system_service # noqa: PLW0602 # pylint: disable=global-variable-not-assigned return deepcopy(__system_service.system_settings) async def get_system_service() -> SystemService: + """Get system service.""" global __system_service # noqa: PLW0602 # pylint: disable=global-variable-not-assigned return __system_service diff --git a/openbb_sdk/sdk/core/openbb_core/api/dependency/user.py b/openbb_sdk/sdk/core/openbb_core/api/dependency/user.py index ffdd8c4337a..67220e3ed61 100644 --- a/openbb_sdk/sdk/core/openbb_core/api/dependency/user.py +++ b/openbb_sdk/sdk/core/openbb_core/api/dependency/user.py @@ -1,5 +1,6 @@ +"""User dependency.""" from datetime import datetime, timedelta -from typing import Annotated, Optional +from typing import Optional from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer @@ -11,6 +12,7 @@ from openbb_core.app.model.user_settings import UserSettings from openbb_core.app.service.user_service import UserService from passlib.context import CryptContext from pymongo import MongoClient +from typing_extensions import Annotated SECRET_KEY = "a0657288545d1d2e991195841782ae2a22574a22954081db0c2888c5f5ddbecc" # nosec # pragma: allowlist secret ALGORITHM = "HS256" @@ -22,10 +24,12 @@ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") def verify_password(plain_password, hashed_password): + """Verify password.""" return pwd_context.verify(plain_password, hashed_password) def get_password_hash(password): + """Get password hash.""" return pwd_context.hash(password) @@ -34,6 +38,7 @@ def authenticate_user( username: str, user_service: UserService, ) -> Optional[UserSettings]: + """Authenticate user.""" user = user_service.user_settings_repository.read_by_profile( filter_list=[("username", username)], ) @@ -52,6 +57,7 @@ def create_access_token( sub: str, expiration_delta: Optional[timedelta] = None, ) -> AccessToken: + """Create access token.""" if expiration_delta is None: expiration_delta = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) @@ -67,6 +73,7 @@ def create_jwt_token( key=SECRET_KEY, algorithm=ALGORITHM, ) -> str: + """Create JWT token.""" claims = access_token.dict() encoded_jwt = jwt.encode(claims=claims, key=key, algorithm=algorithm) return encoded_jwt @@ -75,6 +82,7 @@ def create_jwt_token( async def get_user_service( system_settings: Annotated[SystemSettings, Depends(get_system_settings)] ) -> UserService: + """Get user service.""" global __user_service # pylint: disable=global-statement if __user_service is None: @@ -92,6 +100,7 @@ async def get_user( jwt_token: Annotated[str, Depends(oauth2_scheme)], user_service: Annotated[UserService, Depends(get_user_service)], ): + """Get user.""" credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", diff --git a/openbb_sdk/sdk/core/openbb_core/api/model/access_token.py b/openbb_sdk/sdk/core/openbb_core/api/model/access_token.py index 3fe5988507d..c21d92b5951 100644 --- a/openbb_sdk/sdk/core/openbb_core/api/model/access_token.py +++ b/openbb_sdk/sdk/core/openbb_core/api/model/access_token.py @@ -1,3 +1,4 @@ +"""Access Token Model.""" from datetime import datetime from openbb_core.app.model.abstract.tagged import Tagged @@ -5,10 +6,12 @@ from pydantic import Field class AccessToken(Tagged): - """This will be turn into a JWT Token""" + """Turn into a JWT Token.""" sub: str = Field(description="Subject of the token, here : `UserSettings.id`") exp: datetime = Field(description="Expiration datetime of the `access_token`.") class Config: + """Pydantic Config.""" + validate_assignment = True diff --git a/openbb_sdk/sdk/core/openbb_core/api/model/token_response.py b/openbb_sdk/sdk/core/openbb_core/api/model/token_response.py index a67d7fd087c..d3980f13d81 100644 --- a/openbb_sdk/sdk/core/openbb_core/api/model/token_response.py +++ b/openbb_sdk/sdk/core/openbb_core/api/model/token_response.py @@ -1,6 +1,9 @@ +"""Token response model.""" from pydantic import BaseModel class TokenResponse(BaseModel): + """Token response model.""" + access_token: str token_type: str diff --git a/openbb_sdk/sdk/core/openbb_core/api/router/account.py b/openbb_sdk/sdk/core/openbb_core/api/router/account.py index 3324b7c7ddd..205ec7bc472 100644 --- a/openbb_sdk/sdk/core/openbb_core/api/router/account.py +++ b/openbb_sdk/sdk/core/openbb_core/api/router/account.py @@ -1,5 +1,3 @@ -from typing import Annotated - from fastapi import APIRouter, Depends, Form, HTTPException, status from fastapi.security import OAuth2PasswordRequestForm from openbb_core.api.dependency.user import ( @@ -14,6 +12,7 @@ from openbb_core.api.dependency.user import ( from openbb_core.api.model.token_response import TokenResponse from openbb_core.app.model.user_settings import UserSettings from openbb_core.app.service.hub_service import HubService +from typing_extensions import Annotated router = APIRouter(prefix="/account", tags=["Account"]) diff --git a/openbb_sdk/sdk/core/openbb_core/api/router/commands.py b/openbb_sdk/sdk/core/openbb_core/api/router/commands.py index 8cc676e9427..49ee6a65c5a 100644 --- a/openbb_sdk/sdk/core/openbb_core/api/router/commands.py +++ b/openbb_sdk/sdk/core/openbb_core/api/router/commands.py @@ -1,16 +1,19 @@ +import inspect from functools import partial, wraps from inspect import Parameter, Signature, signature -from typing import Annotated, Any, Callable, Dict, Tuple, TypeVar +from typing import Any, Callable, Dict, Tuple, TypeVar from fastapi import APIRouter, Depends from fastapi.routing import APIRoute from openbb_core.api.dependency.user import get_user from openbb_core.app.command_runner import CommandRunner from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.command_output import CommandOutput from openbb_core.app.model.user_settings import UserSettings from openbb_core.app.router import RouterLoader from openbb_core.app.service.system_service import SystemService -from typing_extensions import ParamSpec +from pydantic import BaseModel +from typing_extensions import Annotated, ParamSpec T = TypeVar("T") P = ParamSpec("P") @@ -73,6 +76,52 @@ def build_new_signature(func): ) +def validate_output(c_out: CommandOutput) -> CommandOutput: + """ + Validate CommandOutput object. + Checks against the CommandOutput schema and removes fields that contain the + `exclude_from_api` extra `pydantic.Field` kwarg. + Note that the modification to the `CommandOutput` object is done in-place. + + Parameters + ---------- + c_out : CommandOutput + CommandOutput object to validate. + + Returns + ------- + CommandOutput + Validated CommandOutput object. + """ + + def is_model(type_): + return inspect.isclass(type_) and issubclass(type_, BaseModel) + + def exclude_fields_from_api(key: str, value: Any): + type_ = type(value) + + # case where 1st layer field needs to be excluded + if key in c_out.__fields__ and c_out.__fields__[key].field_info.extra.get( + "exclude_from_api", None + ): + delattr(c_out, key) + + # if it's a model with nested fields + elif is_model(type_): + for field in type_.__fields__.values(): + if field.field_info.extra.get("exclude_from_api", None): + delattr(value, field.name) + + # if it's a yet a nested model we need to go deeper in the recursion + elif is_model(field.type_): + exclude_fields_from_api(field.name, getattr(value, field.name)) + + for k, v in c_out: + exclude_fields_from_api(k, v) + + return c_out + + def build_api_wrapper( command_runner: CommandRunner, route: APIRoute, @@ -97,7 +146,7 @@ def build_api_wrapper( execute = partial(command_runner.run_once, user_settings, path) journal_entry = execute(*args, **kwargs) - return journal_entry.output + return validate_output(journal_entry.output) return wrapper diff --git a/openbb_sdk/sdk/core/openbb_core/api/router/coverage.py b/openbb_sdk/sdk/core/openbb_core/api/router/coverage.py index 4b3af70b484..9075a370646 100644 --- a/openbb_sdk/sdk/core/openbb_core/api/router/coverage.py +++ b/openbb_sdk/sdk/core/openbb_core/api/router/coverage.py @@ -8,9 +8,11 @@ router = APIRouter(prefix="/coverage", tags=["Coverage"]) @router.get("/providers") async def get_provider_coverage() -> Dict[str, List[str]]: + """Get provider coverage.""" return CommandMap().provider_coverage @router.get("/commands") async def get_command_coverage() -> Dict[str, List[str]]: + """Get command coverage.""" return CommandMap().command_coverage diff --git a/openbb_sdk/sdk/core/openbb_core/api/router/settings.py b/openbb_sdk/sdk/core/openbb_core/api/router/settings.py index c47f7436b20..79e8e844751 100644 --- a/openbb_sdk/sdk/core/openbb_core/api/router/settings.py +++ b/openbb_sdk/sdk/core/openbb_core/api/router/settings.py @@ -1,13 +1,8 @@ -from typing import Annotated - from fastapi import APIRouter, Depends -from openbb_core.api.dependency.user import ( - UserService, - get_user, - get_user_service, -) +from openbb_core.api.dependency.user import UserService, get_user, get_user_service from openbb_core.app.model.credentials import Credentials from openbb_core.app.model.user_settings import UserSettings +from typing_extensions import Annotated router = APIRouter(prefix="/settings", tags=["Settings"]) @@ -16,6 +11,7 @@ router = APIRouter(prefix="/settings", tags=["Settings"]) async def read_users_settings( user_settings: Annotated[UserSettings, Depends(get_user)], ) -> UserSettings: + """Read users settings.""" return user_settings @@ -25,6 +21,7 @@ async def patch_user_credentials( user_settings: Annotated[UserSettings, Depends(get_user)], user_service: Annotated[UserService, Depends(get_user_service)], ) -> UserSettings: + """Patch user credentials.""" current = user_settings.credentials.dict() incoming = credentials.dict(exclude_none=True) current.update(incoming) diff --git a/openbb_sdk/sdk/core/openbb_core/api/router/system.py b/openbb_sdk/sdk/core/openbb_core/api/router/system.py index 8a9c8e834e6..7b3638d97ba 100644 --- a/openbb_sdk/sdk/core/openbb_core/api/router/system.py +++ b/openbb_sdk/sdk/core/openbb_core/api/router/system.py @@ -1,8 +1,8 @@ -from typing import Annotated - +"""System router.""" from fastapi import APIRouter, Depends from openbb_core.api.dependency.system import get_system_settings from openbb_core.app.model.system_settings import SystemSettings +from typing_extensions import Annotated router = APIRouter(prefix="/system", tags=["System"]) @@ -11,4 +11,5 @@ router = APIRouter(prefix="/system", tags=["System"]) async def get_system_model( system_settings: Annotated[SystemSettings, Depends(get_system_settings)], ) -> SystemSettings: + """Get system model.""" return system_settings diff --git a/openbb_sdk/sdk/core/openbb_core/app/charting_manager.py b/openbb_sdk/sdk/core/openbb_core/app/charting_manager.py index 5f6fc25289f..d0f8188d7a8 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/charting_manager.py +++ b/openbb_sdk/sdk/core/openbb_core/app/charting_manager.py @@ -144,10 +144,28 @@ class ChartingManager(metaclass=SingletonMeta): create_backend_func(charting_settings=charting_settings) get_backend_func().start(debug=charting_settings.debug_mode) - def to_plotly_json(self, **kwargs) -> str: + def to_chart(self, **kwargs) -> Chart: """ - Returns the plotly json representation of the chart. + Returns the chart object. + + Parameters + ---------- + **kwargs + Keyword arguments to be passed to the charting extension. + + Returns + ------- + Chart + Chart object. + + Raises + ------ + ChartingManagerError + If charting extension is not installed. + Exception + If the charting extension module does not contain the `to_chart` function. """ + if not self._charting_extension_installed: raise ChartingManagerError( f"Charting extension `{self._charting_extension}` is not installed" @@ -156,12 +174,19 @@ class ChartingManager(metaclass=SingletonMeta): # Dynamically import the charting module backend_module = import_module(self._charting_extension) - # Get the plotly json function from the charting module - to_plotly_json_func = getattr(backend_module, "to_plotly_json") + # Get the `to_chart` function from the charting module + to_chart_func = getattr(backend_module, "to_chart") + # Add the charting settings to the kwargs kwargs["charting_settings"] = self._charting_settings - return to_plotly_json_func(**kwargs) + fig, content = to_chart_func(**kwargs) + + return Chart( + content=content, + format=self.get_chart_format(self._charting_extension), + fig=fig, + ) def chart( self, @@ -211,7 +236,11 @@ class ChartingManager(metaclass=SingletonMeta): kwargs["command_output_item"] = command_output_item kwargs["charting_settings"] = self._charting_settings + charting_function = self.get_chart_function(self._charting_extension, route) + fig, content = charting_function(**kwargs) + return Chart( - content=self.get_chart_function(self._charting_extension, route)(**kwargs), + content=content, format=self.get_chart_format(self._charting_extension), + fig=fig, ) diff --git a/openbb_sdk/sdk/core/openbb_core/app/logs/formatters/formatter_with_exceptions.py b/openbb_sdk/sdk/core/openbb_core/app/logs/formatters/formatter_with_exceptions.py index 2cd52fd4125..c67bf2ae7e6 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/logs/formatters/formatter_with_exceptions.py +++ b/openbb_sdk/sdk/core/openbb_core/app/logs/formatters/formatter_with_exceptions.py @@ -82,8 +82,8 @@ class FormatterWithExceptions(logging.Formatter): return text_mocked - @classmethod - def filter_special_tags(cls, text: str) -> str: + @staticmethod + def filter_special_tags(text: str) -> str: text_filtered = text.replace("\n", " MOCKING_BREAKLINE ") text_filtered = text_filtered.replace("'Traceback", "Traceback") diff --git a/openbb_sdk/sdk/core/openbb_core/app/logs/handlers_manager.py b/openbb_sdk/sdk/core/openbb_core/app/logs/handlers_manager.py index 71c50776c6b..3f2229b13c0 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/logs/handlers_manager.py +++ b/openbb_sdk/sdk/core/openbb_core/app/logs/handlers_manager.py @@ -16,8 +16,6 @@ from openbb_core.app.logs.models.logging_settings import LoggingSettings class HandlersManager: def __init__(self, settings: LoggingSettings): - self.is_initialized = True - self._handlers = settings.handler_list self._settings = settings diff --git a/openbb_sdk/sdk/core/openbb_core/app/logs/models/logging_settings.py b/openbb_sdk/sdk/core/openbb_core/app/logs/models/logging_settings.py index e618167101b..13ef49ccbb3 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/logs/models/logging_settings.py +++ b/openbb_sdk/sdk/core/openbb_core/app/logs/models/logging_settings.py @@ -8,7 +8,14 @@ from openbb_core.app.model.user_settings import UserSettings # pylint: disable=too-many-instance-attributes class LoggingSettings: - def __init__(self, user_settings: UserSettings, system_settings: SystemSettings): + def __init__( + self, + user_settings: Optional[UserSettings] = None, + system_settings: Optional[SystemSettings] = None, + ): + user_settings = user_settings or UserSettings() + system_settings = system_settings or SystemSettings() + user_data_directory = ( str(Path.home() / "OpenBBUserData") if not user_settings.preferences diff --git a/openbb_sdk/sdk/core/openbb_core/app/logs/utils/expired_files.py b/openbb_sdk/sdk/core/openbb_core/app/logs/utils/expired_files.py index 0e4bcb0c523..4f101d23db8 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/logs/utils/expired_files.py +++ b/openbb_sdk/sdk/core/openbb_core/app/logs/utils/expired_files.py @@ -15,8 +15,8 @@ def get_timestamp_from_x_days(x: int) -> float: def get_expired_file_list(directory: Path, before_timestamp: float) -> List[Path]: - expired_files = list() - if directory.exists and directory.is_dir(): # type: ignore + expired_files = [] + if directory.is_dir(): # Check if the directory exists and is a directory for file in directory.iterdir(): if file.is_file() and file.lstat().st_mtime < before_timestamp: expired_files.append(file) diff --git a/openbb_sdk/sdk/core/openbb_core/app/logs/utils/utils.py b/openbb_sdk/sdk/core/openbb_core/app/logs/utils/utils.py index 97548fa5be4..a43ae854dbb 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/logs/utils/utils.py +++ b/openbb_sdk/sdk/core/openbb_core/app/logs/utils/utils.py @@ -24,8 +24,7 @@ def get_app_id(contextual_user_data_directory: str) -> str: print( "Note for macOS users: copy `OpenBB Terminal` folder outside the DMG." ) - else: - raise e + raise e except Exception as e: raise e @@ -35,19 +34,37 @@ def get_app_id(contextual_user_data_directory: str) -> str: def get_log_dir(contextual_user_data_directory: str) -> Path: """Retrieve application's log directory.""" - log_dir = Path(contextual_user_data_directory).joinpath("logs").absolute() + log_dir = create_log_dir_if_not_exists(contextual_user_data_directory) + logging_uuid = create_log_uuid_if_not_exists(log_dir) + uuid_log_dir = create_uuid_dir_if_not_exists(log_dir, logging_uuid) + return uuid_log_dir + + +def create_log_dir_if_not_exists(contextual_user_data_directory: str) -> None: + log_dir = Path(contextual_user_data_directory).joinpath("logs").absolute() if not log_dir.is_dir(): log_dir.mkdir(parents=True, exist_ok=True) - log_id = (log_dir / ".logid").absolute() + return log_dir + +def create_log_uuid_if_not_exists(log_dir: Path) -> str: + log_id = get_log_id(log_dir) if not log_id.is_file(): logging_id = f"{uuid.uuid4()}" log_id.write_text(logging_id, encoding="utf-8") else: logging_id = log_id.read_text(encoding="utf-8").rstrip() + return logging_id + + +def get_log_id(log_dir): + return (log_dir / ".logid").absolute() + + +def create_uuid_dir_if_not_exists(log_dir, logging_id) -> str: uuid_log_dir = (log_dir / logging_id).absolute() if not uuid_log_dir.is_dir(): diff --git a/openbb_sdk/sdk/core/openbb_core/app/model/abstract/singleton.py b/openbb_sdk/sdk/core/openbb_core/app/model/abstract/singleton.py index 39270925bac..6cfc4e74e43 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/model/abstract/singleton.py +++ b/openbb_sdk/sdk/core/openbb_core/app/model/abstract/singleton.py @@ -1,6 +1,9 @@ +from typing import Dict + + class SingletonMeta(type): # TODO : check if we want to update this to be thread safe - _instances = {} + _instances: Dict[object, object] = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: diff --git a/openbb_sdk/sdk/core/openbb_core/app/model/charts/chart.py b/openbb_sdk/sdk/core/openbb_core/app/model/charts/chart.py index e29559a9895..76eabc1cace 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/model/charts/chart.py +++ b/openbb_sdk/sdk/core/openbb_core/app/model/charts/chart.py @@ -2,7 +2,7 @@ from enum import Enum from typing import Any, Dict, Optional from openbb_core.app.model.abstract.error import Error -from pydantic import BaseModel +from pydantic import BaseModel, Field class ChartFormat(str, Enum): @@ -10,28 +10,30 @@ class ChartFormat(str, Enum): class Chart(BaseModel): - content: Optional[Dict[str, Any]] = None - format: Optional[ChartFormat] = ChartFormat.plotly - error: Optional[Error] = None + content: Optional[Dict[str, Any]] = Field( + default=None, + description="Raw textual representation of the chart.", + ) + format: Optional[ChartFormat] = Field( + default=ChartFormat.plotly, + description="Complementary attribute to the `content` attribute. It specifies the format of the chart.", + ) + error: Optional[Error] = Field( + default=None, + description="Exception caught during the computation of the `Chart`.", + ) + fig: Optional[Any] = Field( + default=None, + description="The figure object.", + exclude_from_api=True, + ) class Config: validate_assignment = True - def show(self): - """Shows the chart in PyWry, browser or notebook.""" - - # TODO : this method should be handled by the ChartingManager so that - # this model class doesn't have specific knowledge on the charting extension - - # pylint: disable=import-outside-toplevel - if self.format == ChartFormat.plotly: - from openbb_charting.core.openbb_figure import OpenBBFigure - - chart = OpenBBFigure() - - chart.update(self.content) - - chart.show() - - else: - raise ValueError(f"Chart format {self.format} not supported.") + def __repr__(self) -> str: + return ( + self.__class__.__name__ + + "\n\n" + + "\n".join([f"{k}: {v}" for k, v in self.dict().items()]) + ) diff --git a/openbb_sdk/sdk/core/openbb_core/app/model/charts/charting_settings.py b/openbb_sdk/sdk/core/openbb_core/app/model/charts/charting_settings.py index 843d91cff62..45f423acab1 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/model/charts/charting_settings.py +++ b/openbb_sdk/sdk/core/openbb_core/app/model/charts/charting_settings.py @@ -26,22 +26,25 @@ class ChartingSettings: # System self.log_collect: bool = system_settings.log_collect - self.version = system_settings.version - self.python_version = system_settings.python_version + self.version: str = system_settings.version + self.python_version: str = system_settings.python_version self.test_mode = system_settings.test_mode self.app_id: str = get_app_id(user_data_directory) self.debug_mode: bool = system_settings.debug_mode self.headless: bool = system_settings.headless # User - self.plot_enable_pywry = user_settings.preferences.plot_enable_pywry - self.plot_pywry_width = user_settings.preferences.plot_pywry_width - self.plot_pywry_height = user_settings.preferences.plot_pywry_height - self.plot_open_export = user_settings.preferences.plot_open_export - self.user_email = user_settings.profile.hub_session.email if has_hub else None - self.user_uuid = ( + self.plot_enable_pywry: bool = user_settings.preferences.plot_enable_pywry + self.plot_pywry_width: int = user_settings.preferences.plot_pywry_width + self.plot_pywry_height: int = user_settings.preferences.plot_pywry_height + self.plot_open_export: bool = user_settings.preferences.plot_open_export + self.user_email: Optional[str] = ( + user_settings.profile.hub_session.email if has_hub else None + ) + self.user_uuid: Optional[str] = ( user_settings.profile.hub_session.user_uuid if has_hub else None ) self.user_exports_directory = user_settings.preferences.export_directory self.user_styles_directory = user_settings.preferences.user_styles_directory # Theme - self.chart_style = user_settings.preferences.chart_style + self.chart_style: str = user_settings.preferences.chart_style + self.table_style = user_settings.preferences.table_style diff --git a/openbb_sdk/sdk/core/openbb_core/app/model/command_output.py b/openbb_sdk/sdk/core/openbb_core/app/model/command_output.py index 9d3f9ba7c80..618e17bd628 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/model/command_output.py +++ b/openbb_sdk/sdk/core/openbb_core/app/model/command_output.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Generic, List, Optional, TypeVar +from typing import Dict, Generic, List, Optional, TypeVar, Union import pandas as pd from pydantic import Field @@ -8,8 +8,9 @@ from openbb_core.app.charting_manager import ChartingManager from openbb_core.app.model.abstract.error import Error from openbb_core.app.model.abstract.tagged import Tagged from openbb_core.app.model.abstract.warning import Warning_ -from openbb_core.app.model.charts.chart import Chart, ChartFormat +from openbb_core.app.model.charts.chart import Chart from openbb_core.app.provider_interface import get_provider_interface +from openbb_core.app.utils import basemodel_to_df T = TypeVar("T") PROVIDERS = get_provider_interface().providers_literal @@ -48,26 +49,44 @@ class CommandOutput(GenericModel, Generic[T], Tagged): + "\n".join([f"{k}: {v}" for k, v in self.dict().items()]) ) - def to_dataframe(self) -> pd.DataFrame: + def to_dataframe( + self, concat: bool = True + ) -> Union[pd.DataFrame, Dict[str, pd.DataFrame]]: """Converts results field to pandas dataframe. + Parameters + ---------- + concat : bool, optional + If True, it concatenates the dataframes, by default True. + Returns ------- - pd.DataFrame - Pandas dataframe. + Union[pd.DataFrame, Dict[str, pd.DataFrame]] + Pandas dataframe or dictionary of dataframes. """ if self.results is None: raise OpenBBError("Results not found.") try: - df = pd.DataFrame(self.dict()["results"]) - if "date" in df.columns: - df = df.set_index("date") - df.index = pd.to_datetime(df.index) - except ValueError: - df = pd.DataFrame(self.dict()["results"], index=["values"]).T + res = self.results + if isinstance(res, list): + if isinstance(res[0], dict): + for r in res: + dict_of_df = { + k: basemodel_to_df(v, "date") for k, v in r.items() + } + df = pd.concat(dict_of_df, axis=1) if concat else dict_of_df + + else: + df = basemodel_to_df(res, "date") # type: ignore + else: + df = basemodel_to_df(res, "date") # type: ignore + + # Improve output so that all columns that are None are not returned + df = df.dropna(axis=1, how="all") + except Exception as e: - raise OpenBBError("Failed to convert results to dataframe.") from e + raise OpenBBError("Failed to convert results to DataFrame.") from e return df @@ -79,48 +98,38 @@ class CommandOutput(GenericModel, Generic[T], Tagged): Dict[str, List] Dictionary of lists. """ - df = self.to_dataframe().reset_index() + df = self.to_dataframe().reset_index() # type: ignore results = {} for field in df.columns: results[field] = df[field].tolist() return results - def to_plotly_json( - self, create_chart: bool = True, **kwargs - ) -> Optional[Dict[str, Any]]: + def to_chart(self, **kwargs): """ - Returns the plotly json representation of the chart. - If the chart was already computed it return the plotly json representation of the chart. - Otherwise it computes the chart based on the available data and provided kwargs. + Create or update the `Chart`. + Note that the `chart` attribute is composed by: `content`, `format` and `fig`. Parameters ---------- - create_chart : bool, optional - If True, it creates the chart object and populates the respective field on the object, by default True. **kwargs Keyword arguments to be passed to the charting extension. - This implies that the user has to know the charting extension API; this is the case - because the charting extension may vary on user settings. + This implies that the user has some knowledge on the charting extension API. + This is the case because the charting extension may vary on user preferences. + + Returns + ------- + chart.fig + The chart figure. """ + cm = ChartingManager() + kwargs["data"] = self.to_dataframe() - if self.chart and not kwargs: - plotly_json = self.chart.content - else: - cm = ChartingManager() - kwargs["data"] = self.to_dataframe() - plotly_json = cm.to_plotly_json(**kwargs) - - if create_chart: - try: - self.chart = Chart(content=plotly_json, format=ChartFormat.plotly) - except Exception as e: - self.chart = Chart(error=Error(message=str(e))) - - return plotly_json + self.chart = cm.to_chart(**kwargs) + return self.chart.fig def show(self): """Displays chart.""" - if not self.chart: + if not self.chart and not self.chart.fig: raise OpenBBError("Chart not found.") - self.chart.show() + self.chart.fig.show() diff --git a/openbb_sdk/sdk/core/openbb_core/app/model/credentials.py b/openbb_sdk/sdk/core/openbb_core/app/model/credentials.py index 5d3c5636924..c2527bbdfec 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/model/credentials.py +++ b/openbb_sdk/sdk/core/openbb_core/app/model/credentials.py @@ -1,3 +1,5 @@ +from typing import Dict, List, Optional, Tuple + from pydantic import create_model from openbb_core.app.provider_interface import get_provider_interface @@ -9,12 +11,24 @@ from openbb_core.app.provider_interface import get_provider_interface class Config: validate_assignment = True + # extra = Extra.forbid + + +def format_map( + required_credentials: List[str], +) -> Dict[str, Tuple[object, None]]: + """Format credentials map to be used in the Credentials model""" + formatted: Dict[str, Tuple[object, None]] = {} + for c in required_credentials: + formatted[c] = (Optional[str], None) + + return formatted Credentials = create_model( # type: ignore "Credentials", __config__=Config, - **get_provider_interface().required_credentials, + **format_map(get_provider_interface().required_credentials), ) diff --git a/openbb_sdk/sdk/core/openbb_core/app/model/preferences.py b/openbb_sdk/sdk/core/openbb_core/app/model/preferences.py index f36b8fd4480..d897ed8ae71 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/model/preferences.py +++ b/openbb_sdk/sdk/core/openbb_core/app/model/preferences.py @@ -16,6 +16,8 @@ class Preferences(BaseModel): plot_open_export: bool = ( False # Whether to open plot image exports after they are created ) + table_style: Literal["dark", "light"] = "dark" + request_timeout: PositiveInt = 15 class Config: validate_assignment = True diff --git a/openbb_sdk/sdk/core/openbb_core/app/model/system_settings.py b/openbb_sdk/sdk/core/openbb_core/app/model/system_settings.py index 7d39030216a..b883a7d191b 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/model/system_settings.py +++ b/openbb_sdk/sdk/core/openbb_core/app/model/system_settings.py @@ -20,17 +20,6 @@ FrozenField = partial(Field, allow_mutation=False) class SystemSettings(Tagged): - run_in_isolation: bool = Field( - default=False, - description="Whether or not to run each command in total isolation.", - allow_mutation=False, - ) - dbms_uri: Optional[str] = Field( - default=None, - description="Connection URI like : `mongodb://root:example@localhost:27017/`", - allow_mutation=False, - ) - # System section os: str = FrozenField(default=str(pl.system())) python_version: str = FrozenField(default=str(pl.python_version())) @@ -60,6 +49,14 @@ class SystemSettings(Tagged): test_mode: bool = False debug_mode: bool = False headless: bool = False + run_in_isolation: bool = FrozenField( + default=False, + description="Whether or not to run each command in total isolation.", + ) + dbms_uri: Optional[str] = FrozenField( + default=None, + description="Connection URI like : `mongodb://root:example@localhost:27017/`", + ) class Config: validate_assignment = True diff --git a/openbb_sdk/sdk/core/openbb_core/app/provider_interface.py b/openbb_sdk/sdk/core/openbb_core/app/provider_interface.py index f1a377ff444..0a00c566373 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/provider_interface.py +++ b/openbb_sdk/sdk/core/openbb_core/app/provider_interface.py @@ -1,14 +1,11 @@ from dataclasses import dataclass, make_dataclass -from typing import Any, Dict, List, Literal, Optional, Tuple, Union +from typing import Any, Dict, List, Literal, Optional, Tuple, Type, Union from fastapi import Query -from openbb_provider.map import build_credentials_mapping, build_model_mapping -from openbb_provider.registry import ( - ProviderRegistry, - build_provider_registry, - extensions_dict__, -) +from openbb_provider.query_executor import QueryExecutor +from openbb_provider.registry_map import MapType, RegistryMap from pydantic import BaseConfig, BaseModel, Extra, Field, create_model +from pydantic.fields import ModelField @dataclass @@ -20,21 +17,14 @@ class DataclassField: default: Any -@dataclass -class ExtraParams: - """Extra params dataclass.""" - - @dataclass class StandardParams: """Standard params dataclass.""" @dataclass -class ProviderChoices: - """Provider choices dataclass.""" - - provider: Literal # type: ignore +class ExtraParams: + """Extra params dataclass.""" class StandardData(BaseModel): @@ -45,115 +35,113 @@ class ExtraData(BaseModel): """Extra data model.""" +@dataclass +class ProviderChoices: + """Provider choices dataclass.""" + + provider: Literal # type: ignore + + class ProviderInterface: """Provider interface class. Provides access to 'openbb_provider' package information. Properties ---------- - map : Dict[str, Dict[str, Dict[str, Any]]] + map : MapType Dictionary of provider information. - required_credentials: Dict[str, Tuple[Optional[str], None]] - Dictionary of required_credentials. + required_credentials: List[str] + List of required_credentials. model_providers : Dict[str, ProviderChoices] Dictionary of provider choices by model. params : Dict[str, Dict[str, Union[StandardParams, ExtraParams]]] Dictionary of params by model. - merged_data : Dict[str, BaseModel] - Dictionary of data by model. + return_schema : Dict[str, Type[BaseModel]] + Dictionary of return data schema by model. providers_literal : type Literal of provider names. - provider_choices : type + provider_choices : ProviderChoices Dataclass with literal of provider names. models : List[str] List of model names. Methods ------- - build_registry : ProviderRegistry - Build provider registry + create_executor : QueryExecutor + Create a query executor """ - def __init__(self) -> None: - self.__map = build_model_mapping() - self.__model_providers_map = self.__generate_model_providers_dc() - self.__params = self.__generate_params_dc() - self.__data = self.__generate_data_dc() - self.__merged_data = self.__merge_data_dc(self.__data) - self.__required_credentials = self.__get_required_credentials() + def __init__( + self, + registry_map: Optional[RegistryMap] = None, + query_executor: Optional[QueryExecutor] = None, + ) -> None: + """Initialize provider interface.""" + self._registry_map = registry_map or RegistryMap() + self._query_executor = query_executor or QueryExecutor + + self._map = self._registry_map.map + # TODO: Try these 4 methods in a single iteration + self._model_providers_map = self._generate_model_providers_dc(self._map) + self._params = self._generate_params_dc(self._map) + self._data = self._generate_data_dc(self._map) + self._return_schema = self._generate_return_schema(self._data) + + self._providers_literal = self._get_provider_literal( + self._registry_map.available_providers + ) + self._provider_choices = self._get_provider_choices(self._providers_literal) @property - def map(self) -> Dict[str, Dict[str, Dict[str, Any]]]: + def map(self) -> MapType: """Dictionary of provider information.""" - return self.__map + return self._map @property - def required_credentials(self) -> Dict[str, Tuple[Optional[str], None]]: + def required_credentials(self) -> List[str]: """Dictionary of required credentials by provider.""" - return self.__required_credentials + return self._registry_map.required_credentials @property def model_providers(self) -> Dict[str, ProviderChoices]: """Dictionary of provider choices by model.""" - return self.__model_providers_map + return self._model_providers_map @property def params(self) -> Dict[str, Dict[str, Union[StandardParams, ExtraParams]]]: """Dictionary of params by model.""" - return self.__params + return self._params @property def data(self) -> Dict[str, Dict[str, Union[StandardData, ExtraData]]]: """Dictionary of data by model.""" - return self.__data + return self._data @property - def merged_data(self) -> Dict[str, BaseModel]: + def return_schema(self) -> Dict[str, Type[BaseModel]]: """Dictionary of data by model merged.""" - return self.__merged_data + return self._return_schema @property def providers_literal(self) -> type: """Literal of provider names.""" - providers = [] - for _, provider in self.map.items(): - providers.extend(list(provider.keys())) - providers = list(set(providers)) - if "openbb" in providers: - providers.remove("openbb") - - return Literal[tuple(providers)] # type: ignore + return self._providers_literal @property def provider_choices(self) -> type: """Dataclass with literal of provider names.""" - return make_dataclass( - cls_name="ProviderChoices", - fields=[("provider", self.providers_literal)], - bases=(ProviderChoices,), - ) + return self._provider_choices @property def models(self) -> List[str]: """List of model names.""" - return list(self.__map.keys()) + return self._registry_map.models - def build_registry(self) -> ProviderRegistry: - """Build provider registry.""" - return build_provider_registry(extensions_dict__) + def create_executor(self) -> QueryExecutor: + """Get query executor.""" + return self._query_executor(self._registry_map.registry) # type: ignore @staticmethod - def __get_required_credentials() -> Dict[str, Tuple[Optional[str], None]]: - """Get required credentials.""" - required_credentials: Dict[str, Tuple[Optional[str], None]] = {} - credentials_mapping = build_credentials_mapping() - for credentials in credentials_mapping.values(): - for c in credentials: - required_credentials[c] = (Optional[str], None) - - return required_credentials - - @staticmethod - def __merge_fields( + def _merge_fields( current: DataclassField, incoming: DataclassField, query: bool = False ) -> DataclassField: current_name = current.name @@ -163,7 +151,7 @@ class ProviderInterface: if query: merged_default = Query( default=current.default.default, - description=f"{current.default.description}, {incoming.default.description}", + title=f"{current.default.title}, {incoming.default.title}", ) else: merged_default = current.default @@ -181,35 +169,54 @@ class ProviderInterface: ) @staticmethod - def __create_field( + def _create_field( name: str, - field: Dict[str, Any], - description: Optional[str] = None, + field: ModelField, + provider_name: Optional[str] = None, query: bool = False, + force_optional: bool = False, ) -> DataclassField: new_name = name.replace(".", "_") - type_ = field["type"] + # field.type_ don't work for nested types + # field.outer_type_ don't work for Optional nested types + type_ = field.annotation + description = field.field_info.description + + if field.required: + if force_optional: + type_ = Optional[type_] # type: ignore + default = None + else: + default = ... + else: + default = field.default - default = ... if field["required"] else field["default"] if query: - default = Query(default=default, description=description) - elif description: - default = Field(default=default, description=description) + # We need to use query if we want the field description to show up in the + # swagger, it's a fastapi limitation + default = Query( + default=default, title=provider_name, description=description + ) + elif provider_name: + default = Field( + default=default, title=provider_name, description=description + ) return DataclassField(new_name, type_, default) @classmethod - def __extract_params( + def _extract_params( cls, providers: Any, - ) -> Tuple[Dict[str, Tuple[str, Any, Any]], Dict[str, Tuple[str, Any, Any]]]: + ) -> Tuple[Dict[str, Tuple[str, type, Any]], Dict[str, Tuple[str, type, Any]]]: + """Extract parameters from map.""" standard: Dict[str, Tuple[str, Any, Any]] = {} extra: Dict[str, Tuple[str, Any, Any]] = {} for provider_name, model_details in providers.items(): if provider_name == "openbb": for name, field in model_details["QueryParams"]["fields"].items(): - incoming = cls.__create_field(name, field, query=True) + incoming = cls._create_field(name, field, query=True) standard[incoming.name] = ( incoming.name, @@ -219,22 +226,21 @@ class ProviderInterface: else: for name, field in model_details["QueryParams"]["fields"].items(): if name not in providers["openbb"]["QueryParams"]["fields"]: - incoming = cls.__create_field( - name, field, provider_name, query=True + incoming = cls._create_field( + name, field, provider_name, query=True, force_optional=True ) if incoming.name in extra: current = DataclassField(*extra[incoming.name]) - updated = cls.__merge_fields(current, incoming, query=True) + updated = cls._merge_fields(current, incoming, query=True) else: updated = incoming - if not updated.default.description.startswith( + if not updated.default.title.startswith( "Available for providers:" ): - updated.default.description = ( - "Available for providers: " - + updated.default.description + updated.default.title = ( + "Available for providers: " + updated.default.title ) extra[updated.name] = ( @@ -246,17 +252,17 @@ class ProviderInterface: return standard, extra @classmethod - def __extract_data( + def _extract_data( cls, providers: Any, - ) -> Tuple[Dict[str, Tuple[str, Any, Any]], Dict[str, Tuple[str, Any, Any]]]: + ) -> Tuple[Dict[str, Tuple[str, type, Any]], Dict[str, Tuple[str, type, Any]]]: standard: Dict[str, Tuple[str, Any, Any]] = {} extra: Dict[str, Tuple[str, Any, Any]] = {} for provider_name, model_details in providers.items(): if provider_name == "openbb": for name, field in model_details["Data"]["fields"].items(): - incoming = cls.__create_field(name, field, provider_name) + incoming = cls._create_field(name, field, "openbb") standard[incoming.name] = ( incoming.name, @@ -266,11 +272,13 @@ class ProviderInterface: else: for name, field in model_details["Data"]["fields"].items(): if name not in providers["openbb"]["Data"]["fields"]: - incoming = cls.__create_field(name, field, provider_name) + incoming = cls._create_field( + name, field, provider_name, force_optional=True + ) if incoming.name in extra: current = DataclassField(*extra[incoming.name]) - updated = cls.__merge_fields(current, incoming) + updated = cls._merge_fields(current, incoming) else: updated = incoming @@ -282,12 +290,13 @@ class ProviderInterface: return standard, extra - def __generate_params_dc( - self, + def _generate_params_dc( + self, map_: MapType ) -> Dict[str, Dict[str, Union[StandardParams, ExtraParams]]]: """Generate dataclasses for params. - This creates a dictionary of dataclasses that can be used as extra params. + This creates a dictionary of dataclasses that can be injected as a FastAPI + dependency. Example: ------- @@ -298,16 +307,16 @@ class ProviderInterface: @dataclass class StockNews(ExtraParams): - pageSize: int = Query(default=15, description="benzinga") - displayOutput: int = Query(default="headline", description="benzinga") + pageSize: int = Query(default=15, title="benzinga") + displayOutput: int = Query(default="headline", title="benzinga") ... - sort: str = Query(default=None, description="benzinga, polygon") + sort: str = Query(default=None, title="benzinga, polygon") """ result: Dict = {} # TODO: Consider multiprocessing this loop to speed startup - for model_name, providers in self.__map.items(): - standard, extra = self.__extract_params(providers) + for model_name, providers in map_.items(): + standard, extra = self._extract_params(providers) result[model_name] = { "standard": make_dataclass( # type: ignore @@ -323,11 +332,11 @@ class ProviderInterface: } return result - def __generate_model_providers_dc(self) -> Dict[str, ProviderChoices]: + def _generate_model_providers_dc(self, map_: MapType) -> Dict[str, ProviderChoices]: """Generate dataclasses for provider choices by model. - This creates a dictionary that maps model names to dataclasses that can be used - as provider choices. + This creates a dictionary that maps model names to dataclasses that can be + injected as a FastAPI dependency. Example: ------- @@ -337,7 +346,7 @@ class ProviderInterface: """ result: Dict = {} - for model_name, providers in self.__map.items(): + for model_name, providers in map_.items(): choices = list(providers.keys()) if "openbb" in choices: choices.remove("openbb") @@ -350,12 +359,12 @@ class ProviderInterface: return result - def __generate_data_dc( - self, + def _generate_data_dc( + self, map_: MapType ) -> Dict[str, Dict[str, Union[StandardData, ExtraData]]]: """Generate dataclasses for data. - This creates a dictionary of dataclasses that can be used as data. + This creates a dictionary of dataclasses. Example: ------- @@ -370,9 +379,8 @@ class ProviderInterface: """ result: Dict = {} - # TODO: Consider multiprocessing this loop to speed startup - for model_name, providers in self.__map.items(): - standard, extra = self.__extract_data(providers) + for model_name, providers in map_.items(): + standard, extra = self._extract_data(providers) result[model_name] = { "standard": make_dataclass( # type: ignore cls_name=model_name, @@ -388,10 +396,11 @@ class ProviderInterface: return result - def __merge_data_dc( - self, data: Dict[str, Dict[str, Union[StandardData, ExtraData]]] - ) -> Dict[str, BaseModel]: - """Merge standard data with extra data into a single BaseModel.""" + def _generate_return_schema( + self, + data: Dict[str, Dict[str, Union[StandardData, ExtraData]]], + ) -> Dict[str, Type[BaseModel]]: + """Merge standard data with extra data into a single BaseModel to be injected as FastAPI dependency.""" result: Dict = {} for model_name, dataclasses in data.items(): standard = dataclasses["standard"] @@ -403,27 +412,43 @@ class ProviderInterface: fields_dict: Dict[str, Tuple[Any, Any]] = {} for name, field in fields.items(): fields_dict[name] = ( - field.type_, + field.annotation, Field( - default=field.default, description=field.field_info.description + default=field.default, + title=field.field_info.title, + description=field.field_info.description, ), ) class Config(BaseConfig): extra = Extra.allow - result[model_name] = create_model( # type: ignore + ReturnModel = create_model( # type: ignore model_name, __config__=Config, **fields_dict, # type: ignore ) + # TODO: If we want to support multiple return schemas, we need + # to change this `List` to the schema type + result[model_name] = List[ReturnModel] # type: ignore + return result + def _get_provider_literal(self, available_providers: List[str]) -> type: + return Literal[tuple(available_providers)] # type: ignore + + def _get_provider_choices(self, providers_literal: type) -> type: + return make_dataclass( + cls_name="ProviderChoices", + fields=[("provider", providers_literal)], + bases=(ProviderChoices,), + ) + __provider_interface = ProviderInterface() def get_provider_interface() -> ProviderInterface: - """Get the provider interface.""" + """Get the provider interface, this only needs to be created once.""" return __provider_interface diff --git a/openbb_sdk/sdk/core/openbb_core/app/query.py b/openbb_sdk/sdk/core/openbb_core/app/query.py index 154f26299a3..bf915b1ed09 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/query.py +++ b/openbb_sdk/sdk/core/openbb_core/app/query.py @@ -1,11 +1,9 @@ -"""Helper functions for the builtin extensions.""" +"""Query class.""" import warnings from dataclasses import asdict from typing import Any, Dict -from pydantic import BaseModel - from openbb_core.app.model.abstract.warning import OpenBBWarning from openbb_core.app.model.command_context import CommandContext from openbb_core.app.provider_interface import ( @@ -26,14 +24,16 @@ class Query: standard_params: StandardParams, extra_params: ExtraParams, ) -> None: + """Initialize Query class.""" self.cc = cc self.provider = str(provider_choices.provider) self.standard_params = standard_params self.extra_params = extra_params self.name = self.standard_params.__class__.__name__ + self.provider_interface = get_provider_interface() - @staticmethod def filter_extra_params( + self, extra_params: ExtraParams, provider_name: str, ) -> Dict[str, Any]: @@ -41,15 +41,12 @@ class Query: original = asdict(extra_params) filtered = {} - provider_interface = get_provider_interface() query = extra_params.__class__.__name__ - fields = asdict(provider_interface.params[query]["extra"]()) # type: ignore + fields = asdict(self.provider_interface.params[query]["extra"]()) # type: ignore for k, v in original.items(): f = fields[k] - providers = f.description.replace("Available for providers: ", "").split( - ", " - ) + providers = f.title.replace("Available for providers: ", "").split(", ") if v != f.default: if provider_name in providers: filtered[k] = v @@ -62,28 +59,20 @@ class Query: return filtered - def to_query_params(self, standard_params: StandardParams) -> StandardParams: - """Convert standard params to QueryParams like class.""" - standard_params.__name__ = self.name + "QueryParams" # type: ignore - return standard_params - - def execute(self) -> BaseModel: + def execute(self) -> Any: """Execute the query.""" - - # TODO: Understand if we really need to create the registry in every call - registry = get_provider_interface().build_registry() - creds = self.cc.user_settings.credentials.dict() - query_params = self.to_query_params(self.standard_params) - - filtered = ( + standard_dict = asdict(self.standard_params) + extra_dict = ( self.filter_extra_params(self.extra_params, self.provider) if self.extra_params - else None + else {} ) + query_executor = self.provider_interface.create_executor() - return registry.fetch( + return query_executor.execute( provider_name=self.provider, - query_params=query_params, - extra_params=filtered, - credentials=creds, + model_name=self.name, + params={**standard_dict, **extra_dict}, + credentials=self.cc.user_settings.credentials.dict(), + preferences=self.cc.user_settings.preferences.dict(), ) diff --git a/openbb_sdk/sdk/core/openbb_core/app/router.py b/openbb_sdk/sdk/core/openbb_core/app/router.py index ed4793c666f..d15540fa816 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/router.py +++ b/openbb_sdk/sdk/core/openbb_core/app/router.py @@ -1,17 +1,16 @@ import sys +import warnings from functools import partial from inspect import Parameter, Signature, signature -from types import MappingProxyType from typing import ( - Annotated, Any, Callable, Dict, List, + Mapping, Optional, Type, get_args, - get_origin, get_type_hints, overload, ) @@ -21,7 +20,9 @@ from fastapi import APIRouter, Depends from pydantic import BaseModel from pydantic.config import BaseConfig from pydantic.validators import find_validators +from typing_extensions import Annotated, _AnnotatedAlias +from openbb_core.app.model.abstract.warning import OpenBBWarning from openbb_core.app.model.command_context import CommandContext from openbb_core.app.model.command_output import CommandOutput from openbb_core.app.provider_interface import ( @@ -71,7 +72,7 @@ class CommandValidator: @staticmethod def is_annotated_dc(annotation) -> bool: - return get_origin(annotation) == Annotated and hasattr( + return type(annotation) is _AnnotatedAlias and hasattr( annotation.__args__[0], "__dataclass_fields__" ) @@ -79,7 +80,7 @@ class CommandValidator: def check_reserved_param( name: str, expected_annot: Any, - parameter_map: MappingProxyType[str, Parameter], + parameter_map: Mapping[str, Parameter], func: Callable, sig: Signature, ): @@ -178,7 +179,7 @@ class Router: self, func: Optional[Callable[P, CommandOutput]] = None, **kwargs, - ) -> Callable: + ) -> Optional[Callable]: if func is None: return lambda f: self.command(f, **kwargs) @@ -190,15 +191,15 @@ class Router: kwargs["openapi_extra"] = {"model": model} func = SignatureInspector.complete_signature(func, model) + if func is not None: + CommandValidator.check(func=func) - CommandValidator.check(func=func) + kwargs["path"] = kwargs.get("path", f"/{func.__name__}") + kwargs["endpoint"] = func + kwargs["methods"] = kwargs.get("methods", ["GET"]) + kwargs["description"] = SignatureInspector.get_description(func) - kwargs["path"] = kwargs.get("path", f"/{func.__name__}") - kwargs["endpoint"] = func - kwargs["methods"] = kwargs.get("methods", ["GET"]) - kwargs["description"] = SignatureInspector.get_description(func) - - api_router.add_api_route(**kwargs) + api_router.add_api_route(**kwargs) return func @@ -217,14 +218,18 @@ class SignatureInspector: @classmethod def complete_signature( cls, func: Callable[P, CommandOutput], model: str - ) -> Callable[P, CommandOutput]: + ) -> Optional[Callable[P, CommandOutput]]: """Complete function signature.""" provider_interface = get_provider_interface() if model: if model not in provider_interface.models: - raise AttributeError( - f"Invalid model: '{model}'. Check available models in ProviderInterface().models" + warnings.warn( + message=f"\nSkipping api route '/{func.__name__}'.\n" + f"Model '{model}' not found.\n\n" + "Check available models in ProviderInterface().models", + category=OpenBBWarning, ) + return None cls.validate_signature( func, @@ -253,8 +258,9 @@ class SignatureInspector: callable_=provider_interface.params[model]["extra"], ) - data_type = provider_interface.merged_data[model] - func.__annotations__["return"] = CommandOutput[List[data_type]] # type: ignore + ReturnModel = provider_interface.return_schema[model] + func.__annotations__["return"] = CommandOutput[ReturnModel] # type: ignore + elif ( "provider_choices" in func.__annotations__ and func.__annotations__["provider_choices"] == ProviderChoices @@ -306,11 +312,17 @@ class SignatureInspector: class CommandMap: """Matching Routes with Commands.""" - def __init__(self, router: Optional[Router] = None) -> None: + def __init__( + self, router: Optional[Router] = None, coverage_sep: Optional[str] = None + ) -> None: self._router = router or RouterLoader.from_extensions() self._map = self.get_command_map(router=self._router) - self._provider_coverage = self.get_provider_coverage(router=self._router) - self._command_coverage = self.get_command_coverage(router=self._router) + self._provider_coverage = self.get_provider_coverage( + router=self._router, sep=coverage_sep + ) + self._command_coverage = self.get_command_coverage( + router=self._router, sep=coverage_sep + ) @property def map(self) -> Dict[str, Callable]: @@ -325,13 +337,17 @@ class CommandMap: return self._command_coverage @staticmethod - def get_command_map(router: Router) -> Dict[str, Callable]: + def get_command_map( + router: Router, + ) -> Dict[str, Callable]: api_router = router.api_router command_map = {route.path: route.endpoint for route in api_router.routes} # type: ignore return command_map @staticmethod - def get_provider_coverage(router: Router) -> Dict[str, List[str]]: + def get_provider_coverage( + router: Router, sep: Optional[str] = None + ) -> Dict[str, List[str]]: api_router = router.api_router mapping = get_provider_interface().map @@ -349,12 +365,19 @@ class CommandMap: if provider not in coverage_map: coverage_map[provider] = [] if hasattr(route, "path"): - coverage_map[provider].append(route.path) + rp = ( + route.path + if sep is None + else route.path.replace("/", sep) + ) + coverage_map[provider].append(rp) return coverage_map @staticmethod - def get_command_coverage(router: Router) -> Dict[str, List[str]]: + def get_command_coverage( + router: Router, sep: Optional[str] = None + ) -> Dict[str, List[str]]: api_router = router.api_router mapping = get_provider_interface().map @@ -370,16 +393,17 @@ class CommandMap: providers.remove("openbb") if hasattr(route, "path"): + rp = route.path if sep is None else route.path.replace("/", sep) if route.path not in coverage_map: - coverage_map[route.path] = [] - coverage_map[route.path] = providers + coverage_map[rp] = [] + coverage_map[rp] = providers return coverage_map def get_command(self, route: str) -> Optional[Callable]: return self._map.get(route, None) -class ExtensionError(Exception): +class LoadingError(Exception): pass @@ -394,7 +418,7 @@ class RouterLoader: prefix=f"/{entry_point.name}", ) except Exception as e: - raise ExtensionError( + raise LoadingError( f"Invalid extension '{entry_point.name}': {e}" ) from e diff --git a/openbb_sdk/sdk/core/openbb_core/app/service/hub_service.py b/openbb_sdk/sdk/core/openbb_core/app/service/hub_service.py index 9846cb0f925..b73b687c391 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/service/hub_service.py +++ b/openbb_sdk/sdk/core/openbb_core/app/service/hub_service.py @@ -1,3 +1,4 @@ +"""Hub manager class.""" from typing import Optional import requests @@ -12,11 +13,11 @@ from openbb_core.app.model.user_settings import UserSettings class OpenBBError(Exception): - pass + """OpenBB error.""" class HubService: - """Hub manager class""" + """Manage Hub service class.""" BASE_URL = "https://payments.openbb.dev" TIMEOUT = 10 diff --git a/openbb_sdk/sdk/core/openbb_core/app/service/journal_service.py b/openbb_sdk/sdk/core/openbb_core/app/service/journal_service.py index ff348767de4..b75bf03d1eb 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/service/journal_service.py +++ b/openbb_sdk/sdk/core/openbb_core/app/service/journal_service.py @@ -1,3 +1,4 @@ +"""Journal service.""" from typing import Optional from openbb_core.app.repository.abstract.journal_entry_repository import ( @@ -22,8 +23,11 @@ from pymongo.mongo_client import MongoClient class JournalService: + """Journal service.""" + @staticmethod def valid_client(mongodb_client: Optional[MongoClient] = None) -> bool: + """Check if the client is valid.""" if mongodb_client is None: return False @@ -41,6 +45,7 @@ class JournalService: mongodb_client: Optional[MongoClient] = None, journal_entry_repository: Optional[AbstractJournalEntryRepository] = None, ) -> AbstractJournalEntryRepository: + """Build journal entry repository.""" if journal_entry_repository: pass elif mongodb_client and valid_client: @@ -57,6 +62,7 @@ class JournalService: mongodb_client: Optional[MongoClient] = None, journal_repository: Optional[AbstractJournalRepository] = None, ) -> AbstractJournalRepository: + """Build journal repository.""" if journal_repository: pass elif mongodb_client and valid_client: @@ -87,12 +93,15 @@ class JournalService: @property def mongodb_client(self) -> Optional[MongoClient]: + """Return mongodb client.""" return self._mongodb_client @property def journal_entry_repository(self) -> AbstractJournalEntryRepository: + """Return journal entry repository.""" return self._journal_entry_repository @property def journal_repository(self) -> AbstractJournalRepository: + """Return journal repository.""" return self._journal_repository diff --git a/openbb_sdk/sdk/core/openbb_core/app/service/system_service.py b/openbb_sdk/sdk/core/openbb_core/app/service/system_service.py index f9bc25cc7b9..184e8ea6df6 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/service/system_service.py +++ b/openbb_sdk/sdk/core/openbb_core/app/service/system_service.py @@ -1,3 +1,4 @@ +import json from pathlib import Path from typing import Optional @@ -6,20 +7,36 @@ from openbb_core.app.model.system_settings import SystemSettings class SystemService: + """System service.""" + SYSTEM_SETTINGS_PATH = SYSTEM_SETTINGS_PATH - SYSTEM_SETTINGS_ALLOWED_FIELD_SET = {"run_in_isolation", "dbms_uri"} + SYSTEM_SETTINGS_ALLOWED_FIELD_SET = { + "log_collect", + "test_mode", + "headless", + "debug_mode", + "run_in_isolation", + "dbms_uri", + } @classmethod def read_default_system_settings( cls, path: Optional[Path] = None ) -> SystemSettings: + """Read default system settings.""" path = path or cls.SYSTEM_SETTINGS_PATH if path.exists(): with path.open(mode="r") as file: system_settings_json = file.read() - system_settings = SystemSettings.parse_raw(system_settings_json) + system_settings_dict = json.loads(system_settings_json) + S = system_settings_dict.copy() + for field in S: + if field not in cls.SYSTEM_SETTINGS_ALLOWED_FIELD_SET: + del system_settings_dict[field] + + system_settings = SystemSettings.parse_obj(system_settings_dict) else: system_settings = SystemSettings() @@ -31,6 +48,7 @@ class SystemService: system_settings: SystemSettings, path: Optional[Path] = None, ) -> None: + """Write default system settings.""" path = path or cls.SYSTEM_SETTINGS_PATH system_settings_json = system_settings.json( @@ -50,13 +68,16 @@ class SystemService: @property def system_settings(self) -> SystemSettings: + """Get system settings.""" return self._system_settings @system_settings.setter def system_settings(self, system_settings: SystemSettings) -> None: + """Set system settings.""" self._system_settings = system_settings def refresh_system_settings(self) -> SystemSettings: + """Refresh system settings.""" self._system_settings = self.read_default_system_settings() return self._system_settings diff --git a/openbb_sdk/sdk/core/openbb_core/app/service/user_service.py b/openbb_sdk/sdk/core/openbb_core/app/service/user_service.py index 963d9ca3fb8..e26d2d1ab25 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/service/user_service.py +++ b/openbb_sdk/sdk/core/openbb_core/app/service/user_service.py @@ -1,3 +1,4 @@ +"""User service.""" from functools import reduce from pathlib import Path from typing import Any, Dict, List, MutableMapping, Optional @@ -26,6 +27,8 @@ from pymongo.mongo_client import MongoClient class UserService: + """User service.""" + REPOSITORY_DIRECTORY = Path(__file__).parent.parent.parent.parent USER_SETTINGS_PATH = USER_SETTINGS_PATH USER_SETTINGS_ALLOWED_FIELD_SET = {"credentials", "preferences", "defaults"} @@ -36,6 +39,7 @@ class UserService: mongodb_client: Optional[MongoClient] = None, access_token_repository: Optional[AbstractAccessTokenRepository] = None, ) -> AbstractAccessTokenRepository: + """Build token repository.""" if access_token_repository: pass elif mongodb_client and valid_client: @@ -52,6 +56,7 @@ class UserService: mongodb_client: Optional[MongoClient] = None, user_settings_repository: Optional[AbstractUserSettingsRepository] = None, ) -> AbstractUserSettingsRepository: + """Build user settings repository.""" if user_settings_repository: pass elif mongodb_client and valid_client: @@ -64,6 +69,7 @@ class UserService: @staticmethod def valid_client(client: Optional[MongoClient] = None) -> bool: + """Check if the client is valid.""" if client is None: return False @@ -77,6 +83,7 @@ class UserService: @classmethod def read_default_user_settings(cls, path: Optional[Path] = None) -> UserSettings: + """Read default user settings.""" path = path or cls.USER_SETTINGS_PATH if path.exists(): @@ -95,6 +102,7 @@ class UserService: user_settings: UserSettings, path: Optional[Path] = None, ) -> None: + """Write default user settings.""" path = path or cls.USER_SETTINGS_PATH user_settings_json = user_settings.json( @@ -107,6 +115,7 @@ class UserService: @classmethod def update_default(cls, user_settings: UserSettings) -> UserSettings: + """Update default user settings.""" d1 = cls.read_default_user_settings().dict() d2 = user_settings.dict() if user_settings else {} updated = cls.merge_dicts([d1, d2]) @@ -158,25 +167,31 @@ class UserService: @property def mongodb_client(self) -> Optional[MongoClient]: + """Return MongoDB client.""" return self._mongodb_client @property def default_user_settings(self) -> UserSettings: + """Return default user settings.""" return self._default_user_settings @default_user_settings.setter def default_user_settings(self, default_user_settings: UserSettings) -> None: + """Set default user settings.""" self._default_user_settings = default_user_settings @property def access_token_repository(self) -> AbstractAccessTokenRepository: + """Access token repository.""" return self._token_repository @property def user_settings_repository(self) -> AbstractUserSettingsRepository: + """User settings repository.""" return self._user_settings_repository def refresh_default_default_user_settings(self) -> UserSettings: + """Refresh default default user settings.""" self._default_user_settings = self.read_default_user_settings() return self._default_user_settings diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/account.py b/openbb_sdk/sdk/core/openbb_core/app/static/account.py index 6bd942e5c95..f766e9d1a50 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/account.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/account.py @@ -18,19 +18,21 @@ class OpenBBError(Exception): class Account: - def __init__(self, app: "Container"): - self._app = app + def __init__(self, container: "Container"): + self._container = container + self._openbb_directory = ( + container._command_runner_session.command_runner.system_settings.openbb_directory + ) - def create_hub_service( + def _create_hub_service( self, email: Optional[str] = None, password: Optional[str] = None, sdk_token: Optional[str] = None, ) -> HubService: + """Create hub service to handle connection.""" if email is None and password is None and sdk_token is None: - session_file = Path( - self._app.system.openbb_directory, ".sdk_hub_session.json" - ) + session_file = Path(self._openbb_directory, ".sdk_hub_session.json") if not session_file.exists(): raise OpenBBError("Session not found.") @@ -69,22 +71,22 @@ class Account: UserSettings User settings: profile, credentials, preferences """ - hs = self.create_hub_service(email, password, sdk_token) + hs = self._create_hub_service(email, password, sdk_token) incoming = hs.pull() updated = UserService.update_default(incoming) - self._app._command_runner_session = CommandRunnerSession(user_settings=updated) + self._container._command_runner_session = CommandRunnerSession( + user_settings=updated + ) if remember_me: - Path(self._app.system.openbb_directory).mkdir(parents=False, exist_ok=True) - session_file = Path( - self._app.system.openbb_directory, ".sdk_hub_session.json" - ) + Path(self._openbb_directory).mkdir(parents=False, exist_ok=True) + session_file = Path(self._openbb_directory, ".sdk_hub_session.json") with open(session_file, "w") as f: if not hs.session: raise OpenBBError("Not connected to hub.") json.dump(hs.session.dict(), f, indent=4) - return self._app._command_runner_session.user_settings + return self._container._command_runner_session.user_settings def save(self) -> UserSettings: """Save user settings. @@ -95,16 +97,16 @@ class Account: User settings: profile, credentials, preferences """ hub_session = ( - self._app._command_runner_session.user_settings.profile.hub_session + self._container._command_runner_session.user_settings.profile.hub_session ) if not hub_session: UserService.write_default_user_settings( - self._app._command_runner_session.user_settings + self._container._command_runner_session.user_settings ) else: hs = HubService(hub_session) - hs.push(self._app._command_runner_session.user_settings) - return self._app._command_runner_session.user_settings + hs.push(self._container._command_runner_session.user_settings) + return self._container._command_runner_session.user_settings def refresh(self) -> UserSettings: """Refresh user settings. @@ -115,19 +117,19 @@ class Account: User settings: profile, credentials, preferences """ hub_session = ( - self._app._command_runner_session.user_settings.profile.hub_session + self._container._command_runner_session.user_settings.profile.hub_session ) if not hub_session: - self._app._command_runner_session = CommandRunnerSession() + self._container._command_runner_session = CommandRunnerSession() else: hs = HubService(hub_session) incoming = hs.pull() updated = UserService.update_default(incoming) - updated.id = self._app._command_runner_session.user_settings.id - self._app._command_runner_session = CommandRunnerSession( + updated.id = self._container._command_runner_session.user_settings.id + self._container._command_runner_session = CommandRunnerSession( user_settings=updated ) - return self._app._command_runner_session.user_settings + return self._container._command_runner_session.user_settings def logout(self) -> UserSettings: """Logout from hub. @@ -138,7 +140,7 @@ class Account: User settings: profile, credentials, preferences """ hub_session = ( - self._app._command_runner_session.user_settings.profile.hub_session + self._container._command_runner_session.user_settings.profile.hub_session ) if not hub_session: raise OpenBBError("Not connected to hub.") @@ -146,9 +148,9 @@ class Account: hs = HubService(hub_session) hs.disconnect() - session_file = Path(self._app.system.openbb_directory, ".sdk_hub_session.json") + session_file = Path(self._openbb_directory, ".sdk_hub_session.json") if session_file.exists(): session_file.unlink() - self._app._command_runner_session = CommandRunnerSession() - return self._app._command_runner_session.user_settings + self._container._command_runner_session = CommandRunnerSession() + return self._container._command_runner_session.user_settings diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/app_factory.py b/openbb_sdk/sdk/core/openbb_core/app/static/app_factory.py index 41d3defbb3b..fb6a79154e8 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/app_factory.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/app_factory.py @@ -1,3 +1,4 @@ +"""App factory.""" # pylint: disable=W0231:super-init-not-called from openbb_core.app.command_runner import CommandRunnerSession @@ -8,9 +9,10 @@ from openbb_core.app.static.coverage import Coverage def create_app(): + """Create the app.""" try: # pylint: disable=import-outside-toplevel - from openbb_core.app.static.package.__commands__ import Commands + from openbb_core.app.static.package.__extensions__ import Extensions except ImportError as e: raise Exception( "If you are seeing this exception, you should probably be doing: " @@ -18,7 +20,7 @@ def create_app(): "PackageBuilder.build()" ) from e - class App(Commands): + class App(Extensions): """Here you can access all the basic utility menus and extensions. Basic utility menus: diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/container.py b/openbb_sdk/sdk/core/openbb_core/app/static/container.py index 75cd907ab74..43da30fb6ed 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/container.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/container.py @@ -1,6 +1,9 @@ +"""Container class.""" from openbb_core.app.command_runner import CommandRunnerSession class Container: + """Container class for the command runner session.""" + def __init__(self, command_runner_session: CommandRunnerSession) -> None: self._command_runner_session = command_runner_session diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/coverage.py b/openbb_sdk/sdk/core/openbb_core/app/static/coverage.py index 3af92cd7e14..a537baa9962 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/coverage.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/coverage.py @@ -1,14 +1,19 @@ +"""Coverage module.""" from openbb_core.app.router import CommandMap class Coverage: + """Coverage class.""" + def __init__(self): - self.__command_map = CommandMap() + self._command_map = CommandMap(coverage_sep=".") @property def providers(self): - return self.__command_map.provider_coverage + """Return providers coverage.""" + return self._command_map.provider_coverage @property def commands(self): - return self.__command_map.command_coverage + """Return commands coverage.""" + return self._command_map.command_coverage diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/filters.py b/openbb_sdk/sdk/core/openbb_core/app/static/filters.py index 8af1c1eb2bd..54fb66976f5 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/filters.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/filters.py @@ -1,3 +1,5 @@ +"""OpenBB filters.""" + import builtins from functools import wraps @@ -10,10 +12,12 @@ from openbb_core.app.utils import df_to_basemodel class OpenBBError(Exception): - pass + """A custom exception for OpenBB errors.""" def filter_call(func): + """Filter command call.""" + @wraps(wrapped=func) def inner(*args, **kwargs): self = args[0] @@ -28,7 +32,7 @@ def filter_call(func): msg = "" for error in e.errors(): - msg += f"{error['loc'][-1]} -> {error['msg']}" + msg += f"\narg: {error['loc'][-1]} -> {error['msg']}" raise OpenBBError(msg) from e @@ -36,7 +40,7 @@ def filter_call(func): def filter_inputs(**kwargs) -> dict: - """Filter command inputs""" + """Filter command inputs.""" for key, value in kwargs.items(): if isinstance(value, pd.DataFrame): kwargs[key] = df_to_basemodel(value, index=True) @@ -45,7 +49,7 @@ def filter_inputs(**kwargs) -> dict: def filter_output(command_output: CommandOutput) -> CommandOutput: - """Filter command output""" + """Filter command output.""" if command_output.warnings: for w in command_output.warnings: category = getattr(builtins, w.category, OpenBBWarning) diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/package/__commands__.py b/openbb_sdk/sdk/core/openbb_core/app/static/package/__extensions__.py similarity index 98% rename from openbb_sdk/sdk/core/openbb_core/app/static/package/__commands__.py rename to openbb_sdk/sdk/core/openbb_core/app/static/package/__extensions__.py index d059a295ecb..fc3fea757a4 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/package/__commands__.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/package/__extensions__.py @@ -4,7 +4,7 @@ from openbb_core.app.static.container import Container -class Commands(Container): +class Extensions(Container): @property def crypto(self): # route = "/crypto" from openbb_core.app.static.package import crypto diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/package/crypto.py b/openbb_sdk/sdk/core/openbb_core/app/static/package/crypto.py index beb48305792..ab981aa98fc 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/package/crypto.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/package/crypto.py @@ -25,79 +25,98 @@ class CLASS_crypto(Container): ) -> CommandOutput[typing.List]: """Crypto Intraday Price. - Available providers: fmp, polygon - Standard - ======== + openbb + ====== - Parameter - --------- - symbol : str - The symbol of the company. + Parameters + ---------- + provider: Literal[fmp, polygon] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. start_date : Optional[date] - The start date of the stock data from which to retrieve the data. + Start date of the data, in YYYY-MM-DD format. end_date : Optional[date] - The end date of the stock data up to which to retrieve the data. - + End date of the data, in YYYY-MM-DD format. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + CryptoEOD + --------- date : datetime - The date of the stock. + The date of the data. open : PositiveFloat - The open price of the stock. + The open price of the symbol. high : PositiveFloat - The high price of the stock. + The high price of the symbol. low : PositiveFloat - The low price of the stock. + The low price of the symbol. close : PositiveFloat - The close price of the stock. - adj_close : Optional[PositiveFloat] - The adjusted close price of the stock. + The close price of the symbol. volume : PositiveFloat - The volume of the stock. + The volume of the symbol. + vwap : PositiveFloat + Volume Weighted Average Price of the symbol. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/#Cryptocurrencies + Parameters + ---------- + timeseries : Optional[NonNegativeInt] + Number of days to look back. - Parameter + + CryptoEOD --------- - timeseries : Optional[int] - The number of days to look back. - serietype : Optional[Literal["line"]] - The type of the series. Only "line" is supported. - - - Returns - ------- - Documentation not available. - + adjClose : float + Adjusted Close Price of the symbol. + unadjustedVolume : float + Unadjusted volume of the symbol. + change : float + Change in the price of the symbol from the previous day. + changePercent : float + Change \\% in the price of the symbol. + label : str + Human readable format of the date. + changeOverTime : float + Change \\% in the price of the symbol over a period of time. polygon ======= - Source: https://polygon.io/docs/crypto/get_v2_aggs_ticker__cryptoticker__range__multiplier___timespan___from___to - Parameters ---------- - timespan : Timespan, optional - The timespan of the query, by default Timespan.day - sort : Literal["asc", "desc"], optional - The sort order of the query, by default "desc" - limit : PositiveInt, optional - The limit of the query, by default 49999 - adjusted : bool, optional - Whether the query is adjusted, by default True - multiplier : PositiveInt, optional - The multiplier of the query, by default 1 + timespan : Literal['minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'] + The timespan of the data. + sort : Literal['asc', 'desc'] + Sort order of the data. + limit : PositiveInt + The number of data entries to return. + adjusted : bool + Whether the data is adjusted. + multiplier : PositiveInt + The multiplier of the timespan. - Returns - ------- - Documentation not available. - """ + CryptoEOD + --------- + n : PositiveInt + The number of transactions for the symbol in the time period.""" inputs = filter_inputs( provider_choices={ "provider": provider, diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/package/economy.py b/openbb_sdk/sdk/core/openbb_core/app/static/package/economy.py index ce7fd38bc5f..b7a7b96cbc8 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/package/economy.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/package/economy.py @@ -19,7 +19,7 @@ class CLASS_economy(Container): def corecpi( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """CORECPI.""" inputs = filter_inputs( @@ -45,49 +45,64 @@ class CLASS_economy(Container): provider: Optional[Literal["fmp"]] = None, **kwargs ) -> CommandOutput[typing.List]: - """ + """Get the constituents of an index. - Available providers: fmp, - Standard - ======== + openbb + ====== - Parameter - --------- + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. index : Literal['nasdaq', 'sp500', 'dowjones'] - The index for which we want to fetch the constituents. Default is 'dowjones'. - + The index for which we want to fetch the constituents. Returns ------- - symbol : str - The symbol of the constituent company in the index. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + MajorIndicesConstituents + ------------------------ + symbol : ConstrainedStrValue + Symbol to get data for. name : str The name of the constituent company in the index. sector : str The sector the constituent company in the index belongs to. - subSector : str + sub_sector : Optional[str] The sub-sector the constituent company in the index belongs to. - headQuarter : str + headquarter : Optional[str] The location of the headquarter of the constituent company in the index. - dateFirstAdded : date + date_first_added : Union[date, str, NoneType] The date the constituent company was added to the index. cik : int The Central Index Key of the constituent company in the index. - founded : date + founded : Union[date, str] The founding year of the constituent company in the index. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/list-of-dow-companies-api/ - https://site.financialmodelingprep.com/developer/docs/list-of-sp-500-companies-api/ - https://site.financialmodelingprep.com/developer/docs/list-of-nasdaq-companies-api/ - - Parameter - --------- + Parameters + ---------- All fields are standardized. - """ + + + MajorIndicesConstituents + ------------------------ + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -174,52 +189,64 @@ class CLASS_economy(Container): ) -> CommandOutput[typing.List]: """CPI. - Available providers: fred, - Standard - ======== - When other provders are added, this will probably need less strict types + openbb + ====== - Parameter - --------- - countries: List[CPI_COUNTRIES] - The country or countries you want to see. - units: List[CPI_UNITS] - The units you want to see, can be "growth_previous", "growth_same" or "index_2015". - frequency: List[CPI_FREQUENCY] - The frequency you want to see, either "annual", monthly" or "quarterly". - harmonized: bool + Parameters + ---------- + provider: Literal[fred] + The provider to use for the query. + countries : List[Literal['australia', 'austria', 'belgium', 'brazil', 'bulgaria', 'canada', 'chile', 'china', 'croatia', 'cyprus', 'czech_republic', 'denmark', 'estonia', 'euro_area', 'finland', 'france', 'germany', 'greece', 'hungary', 'iceland', 'india', 'indonesia', 'ireland', 'israel', 'italy', 'japan', 'korea', 'latvia', 'lithuania', 'luxembourg', 'malta', 'mexico', 'netherlands', 'new_zealand', 'norway', 'poland', 'portugal', 'romania', 'russian_federation', 'slovak_republic', 'slovakia', 'slovenia', 'south_africa', 'spain', 'sweden', 'switzerland', 'turkey', 'united_kingdom', 'united_states']] + The country or countries to get data. + units : Literal['growth_previous', 'growth_same', 'index_2015'] + The data units. + frequency : Literal['monthly', 'quarterly', 'annual'] + The data time frequency. + harmonized : bool Whether you wish to obtain harmonized data. - start_date: Optional[date] - Start date, formatted YYYY-MM-DD - end_date: Optional[date] - End date, formatted YYYY-MM-DD - + start_date : Optional[date] + Start date of the data, in YYYY-MM-DD format. + end_date : Optional[date] + End date of the data, in YYYY-MM-DD format. Returns ------- - Documentation not available. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + CPI + --- + date : date + The date of the data. + realtime_start : date + The date the data was updated. + realtime_end : date + The date the data was updated. + value : float + The value of the data. + fred ==== - When other provders are added, this will probably need less strict types - Parameter - --------- - countries: List[CPI_COUNTRIES] - The country or countries you want to see. - units: List[CPI_UNITS] - The units you want to see, can be "growth_previous", "growth_same" or "index_2015". - frequency: List[CPI_FREQUENCY] - The frequency you want to see, either "annual", monthly" or "quarterly". - harmonized: bool - Whether you wish to obtain harmonized data. - start_date: Optional[date] - Start date, formatted YYYY-MM-DD - end_date: Optional[date] - End date, formatted YYYY-MM-DD - """ + Parameters + ---------- + All fields are standardized. + + + CPI + --- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -248,7 +275,7 @@ class CLASS_economy(Container): def cpi_options( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """Get the options for v3 cpi(options=True)""" inputs = filter_inputs( @@ -270,75 +297,118 @@ class CLASS_economy(Container): def index( self, symbol: str, + start_date: Union[datetime.date, None, str] = None, + end_date: Union[datetime.date, None, str] = None, chart: bool = False, provider: Optional[Literal["fmp", "polygon"]] = None, **kwargs ) -> CommandOutput[typing.List]: """Get OHLCV data for an index. - Available providers: fmp, polygon - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the index. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp, polygon] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + start_date : Optional[date] + Start date of the data, in YYYY-MM-DD format. + end_date : Optional[date] + End date of the data, in YYYY-MM-DD format. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + MajorIndicesEOD + --------------- open : PositiveFloat - The open price of the stock. + The open price of the symbol. high : PositiveFloat - The high price of the stock. + The high price of the symbol. low : PositiveFloat - The low price of the stock. + The low price of the symbol. close : PositiveFloat - The close price of the stock. - date : datetime - The date of the stock. + The close price of the symbol. + volume : PositiveFloat + The volume of the symbol. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/#Historical-stock-index-prices + Parameters + ---------- + timeseries : Optional[NonNegativeInt] + Number of days to look back. - Parameter - --------- - interval : Literal['1min', '5min', '15min', '30min', '1hour', '4hour'] - The interval of the index data to fetch. Default is '1hour`. + MajorIndicesEOD + --------------- + date : datetime + The date of the data. + adjClose : float + Adjusted Close Price of the symbol. + unadjustedVolume : float + Unadjusted volume of the symbol. + change : float + Change in the price of the symbol from the previous day. + changePercent : float + Change \\% in the price of the symbol. + vwap : float + Volume Weighted Average Price of the symbol. + label : str + Human readable format of the date. + changeOverTime : float + Change \\% in the price of the symbol over a period of time. polygon ======= - Source: https://polygon.io/docs/indices/getting-started - Parameters ---------- - start_date : Union[date, datetime] - The start date of the query. - end_date : Union[date, datetime] - The end date of the query. - timespan : Timespan, optional - The timespan of the query, by default Timespan.day - sort : Literal["asc", "desc"], optional - The sort order of the query, by default "desc" - limit : PositiveInt, optional - The limit of the query, by default 49999 - adjusted : bool, optional - Whether the query is adjusted, by default True - multiplier : PositiveInt, optional - The multiplier of the query, by default 1 - """ + timespan : Literal['minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'] + The timespan of the data. + sort : Literal['asc', 'desc'] + Sort order of the data. + limit : PositiveInt + The number of data entries to return. + adjusted : bool + Whether the data is adjusted. + multiplier : PositiveInt + The multiplier of the timespan. + + + MajorIndicesEOD + --------------- + t : datetime + The timestamp of the data. + n : PositiveInt + The number of transactions for the symbol in the time period. + vw : PositiveFloat + The volume weighted average price of the symbol.""" inputs = filter_inputs( provider_choices={ "provider": provider, }, standard_params={ "symbol": symbol, + "start_date": start_date, + "end_date": end_date, }, extra_params=kwargs, chart=chart, @@ -358,18 +428,35 @@ class CLASS_economy(Container): ) -> CommandOutput[typing.List]: """AVAILABLE_INDICES. - Available providers: fmp, - Standard - ======== + openbb + ====== - - Returns the major indices from Dow Jones, Nasdaq and, S&P 500. + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + All fields are standardized. Returns ------- - symbol : str - The symbol of the index. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + AvailableIndices + ---------------- + symbol : ConstrainedStrValue + Symbol to get data for. name : Optional[str] The name of the index. currency : Optional[str] @@ -382,9 +469,14 @@ class CLASS_economy(Container): fmp === - Source: https://site.financialmodelingprep.com/developer/docs/#Historical-stock-index-prices + Parameters + ---------- + All fields are standardized. - """ + + AvailableIndices + ---------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -408,25 +500,53 @@ class CLASS_economy(Container): ) -> CommandOutput[typing.List]: """Market Risk Premium. - Available providers: fmp, - Standard - ======== + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + All fields are standardized. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + RiskPremium + ----------- country : str - continent : str - totalEquityRiskPremium : PositiveFloat - countryRiskPremium : PositiveFloat + Market country. + continent : Optional[str] + Continent of the country. + total_equity_risk_premium : PositiveFloat + The total equity risk premium for the country. + country_risk_premium : NonNegativeFloat + The country-specific risk premium. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/market-risk-premium-api/ + Parameters + ---------- + All fields are standardized. - """ + + RiskPremium + ----------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -448,7 +568,7 @@ class CLASS_economy(Container): def macro( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """Query EconDB for macro data.""" inputs = filter_inputs( @@ -470,7 +590,7 @@ class CLASS_economy(Container): def macro_countries( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """MACRO_COUNTRIES.""" inputs = filter_inputs( @@ -492,7 +612,7 @@ class CLASS_economy(Container): def macro_parameters( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """MACRO_PARAMETERS.""" inputs = filter_inputs( @@ -514,7 +634,7 @@ class CLASS_economy(Container): def balance( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """BALANCE.""" inputs = filter_inputs( @@ -536,7 +656,7 @@ class CLASS_economy(Container): def bigmac( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """BIGMAC.""" inputs = filter_inputs( @@ -558,7 +678,7 @@ class CLASS_economy(Container): def country_codes( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """COUNTRY_CODES.""" inputs = filter_inputs( @@ -580,7 +700,7 @@ class CLASS_economy(Container): def currencies( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """CURRENCIES.""" inputs = filter_inputs( @@ -602,7 +722,7 @@ class CLASS_economy(Container): def debt( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """DEBT.""" inputs = filter_inputs( @@ -624,7 +744,7 @@ class CLASS_economy(Container): def events( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """EVENTS.""" inputs = filter_inputs( @@ -646,7 +766,7 @@ class CLASS_economy(Container): def fgdp( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """FGDP.""" inputs = filter_inputs( @@ -668,7 +788,7 @@ class CLASS_economy(Container): def fred( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """FRED.""" inputs = filter_inputs( @@ -690,7 +810,7 @@ class CLASS_economy(Container): def fred_search( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """FRED Search (was fred_notes).""" inputs = filter_inputs( @@ -712,7 +832,7 @@ class CLASS_economy(Container): def futures( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """FUTURES. 2 sources""" inputs = filter_inputs( @@ -734,7 +854,7 @@ class CLASS_economy(Container): def gdp( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """GDP.""" inputs = filter_inputs( @@ -756,7 +876,7 @@ class CLASS_economy(Container): def glbonds( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """GLBONDS.""" inputs = filter_inputs( @@ -778,7 +898,7 @@ class CLASS_economy(Container): def indices( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """INDICES.""" inputs = filter_inputs( @@ -800,7 +920,7 @@ class CLASS_economy(Container): def overview( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """OVERVIEW.""" inputs = filter_inputs( @@ -822,7 +942,7 @@ class CLASS_economy(Container): def perfmap( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """PERFMAP.""" inputs = filter_inputs( @@ -844,7 +964,7 @@ class CLASS_economy(Container): def performance( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """PERFORMANCE.""" inputs = filter_inputs( @@ -866,7 +986,7 @@ class CLASS_economy(Container): def revenue( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """REVENUE.""" inputs = filter_inputs( @@ -888,7 +1008,7 @@ class CLASS_economy(Container): def rgdp( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """RGDP.""" inputs = filter_inputs( @@ -910,7 +1030,7 @@ class CLASS_economy(Container): def rtps( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """RTPS.""" inputs = filter_inputs( @@ -932,7 +1052,7 @@ class CLASS_economy(Container): def search_index( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """SEARCH_INDEX.""" inputs = filter_inputs( @@ -954,7 +1074,7 @@ class CLASS_economy(Container): def spending( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """SPENDING.""" inputs = filter_inputs( @@ -976,7 +1096,7 @@ class CLASS_economy(Container): def trust( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """TRUST.""" inputs = filter_inputs( @@ -998,7 +1118,7 @@ class CLASS_economy(Container): def usbonds( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """USBONDS.""" inputs = filter_inputs( @@ -1020,7 +1140,7 @@ class CLASS_economy(Container): def valuation( self, chart: bool = False, - provider: Optional[Literal["fred", "polygon", "benzinga", "fmp"]] = None, + provider: Optional[Literal["benzinga", "fmp", "fred", "polygon"]] = None, ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """VALUATION.""" inputs = filter_inputs( diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/package/fixedincome.py b/openbb_sdk/sdk/core/openbb_core/app/static/package/fixedincome.py index a46f8309ceb..5faacc61adf 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/package/fixedincome.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/package/fixedincome.py @@ -1,7 +1,8 @@ ### THIS FILE IS AUTO-GENERATED. DO NOT EDIT. ### +import datetime import typing -from typing import Literal, Optional +from typing import Literal, Optional, Union from pydantic import validate_arguments @@ -17,31 +18,46 @@ class CLASS_fixedincome(Container): @validate_arguments def treasury( self, - start_date: Optional[str] = None, - end_date: Optional[str] = "2023-07-24", + start_date: Union[datetime.date, None, str] = None, + end_date: Union[datetime.date, None, str] = None, chart: bool = False, provider: Optional[Literal["fmp"]] = None, **kwargs ) -> CommandOutput[typing.List]: """Get treasury rates. - Available providers: fmp, - Standard - ======== - - Parameter - --------- - start_date : Optional[str] - Start date of the data, default is None. - end_date : Optional[str] - End date of the data, default is today. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + start_date : Optional[date] + Start date of the data, in YYYY-MM-DD format. + end_date : Optional[date] + End date of the data, in YYYY-MM-DD format. Returns ------- - date : dateType - The date of the treasury rates. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + TreasuryRates + ------------- + date : date + The date of the data. month_1 : float The 1 month treasury rate. month_2 : float @@ -70,17 +86,14 @@ class CLASS_fixedincome(Container): fmp === - Source: https://site.financialmodelingprep.com/developer/docs/treasury-rates-api/ - - Parameter - --------- + Parameters + ---------- All fields are standardized. - Returns - ------- - Documentation not available. - """ + TreasuryRates + ------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/package/forex.py b/openbb_sdk/sdk/core/openbb_core/app/static/package/forex.py index d94e6428ddc..b40dd208edc 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/package/forex.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/package/forex.py @@ -1,7 +1,8 @@ ### THIS FILE IS AUTO-GENERATED. DO NOT EDIT. ### +import datetime import typing -from typing import Literal, Optional +from typing import Literal, Optional, Union from pydantic import validate_arguments @@ -14,35 +15,101 @@ class CLASS_forex(Container): @filter_call @validate_arguments def pairs( - self, chart: bool = False, provider: Optional[Literal["fmp"]] = None, **kwargs + self, + chart: bool = False, + provider: Optional[Literal["fmp", "polygon"]] = None, + **kwargs ) -> CommandOutput[typing.List]: """Forex Available Pairs. - Available providers: fmp, - Standard - ======== + openbb + ====== + Parameters + ---------- + provider: Literal[fmp, polygon] + The provider to use for the query. + All fields are standardized. Returns ------- - symbol : str - The symbol of the currency pair. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + ForexPairs + ---------- name : str - The name of the currency pair separated by '/'. - currency : str - The base currency of the currency pair. - stock_exchange : Optional[str] - The stock exchange of the currency pair. - exchange_short_name : Optional[str] - The short name of the stock exchange of the currency pair. + The name of the currency pair. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/#Historical-Forex-Price + Parameters + ---------- + All fields are standardized. - """ + + ForexPairs + ---------- + symbol : str + The symbol of the currency pair. + currency : str + The base currency of the currency pair. + stockExchange : Optional[str] + The stock exchange of the currency pair. + exchange_short_name : Optional[str] + The short name of the stock exchange of the currency pair. + + polygon + ======= + + Parameters + ---------- + symbol : Optional[str] + Symbol of the pair to search. + date : Optional[date] + A specific date to get data for. + search : Optional[str] + Search for terms within the ticker and/or company name. + active : Optional[Literal[True, False]] + Specify if the tickers returned should be actively traded on the queried date. + order : Optional[Literal['asc', 'desc']] + Order data by ascending or descending. + sort : Optional[Literal['ticker', 'name', 'market', 'locale', 'currency_symbol', 'currency_name', 'base_currency_symbol', 'base_currency_name', 'last_updated_utc', 'delisted_utc']] + Sort field used for ordering. + limit : Optional[PositiveInt] + The number of data entries to return. + + + ForexPairs + ---------- + market : str + The name of the trading market. Always 'fx'. + locale : str + The locale of the currency pair. + currency_symbol : str + The symbol of the quote currency. + currency_name : str + The name of the quote currency. + base_currency_symbol : str + The symbol of the base currency. + base_currency_name : str + The name of the base currency. + last_updated_utc : datetime + The last updated timestamp in UTC. + delisted_utc : Optional[datetime] + The delisted timestamp in UTC.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -64,88 +131,113 @@ class CLASS_forex(Container): def load( self, symbol: str, + start_date: Union[datetime.date, None, str] = None, + end_date: Union[datetime.date, None, str] = None, chart: bool = False, provider: Optional[Literal["fmp", "polygon"]] = None, **kwargs ) -> CommandOutput[typing.List]: """Forex Intraday Price. - Available providers: fmp, polygon - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the forex currency pair. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp, polygon] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + start_date : Optional[date] + Start date of the data, in YYYY-MM-DD format. + end_date : Optional[date] + End date of the data, in YYYY-MM-DD format. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + ForexEOD + -------- date : datetime - The date of the forex currency pair. + The date of the data. open : PositiveFloat - The open price of the forex currency pair. + The open price of the symbol. high : PositiveFloat - The high price of the forex currency pair. + The high price of the symbol. low : PositiveFloat - The low price of the forex currency pair. + The low price of the symbol. close : PositiveFloat - The close price of the forex currency pair. - adj_close : Optional[PositiveFloat] - The adjusted close price of the forex currency pair. + The close price of the symbol. + volume : NonNegativeFloat + The volume of the symbol. + vwap : Optional[PositiveFloat] + Volume Weighted Average Price of the symbol. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/#Historical-Forex-Price - - Parameter - --------- + Parameters + ---------- All fields are standardized. - Returns - ------- - Documentation not available. - + ForexEOD + -------- + adjClose : float + Adjusted Close Price of the symbol. + unadjustedVolume : float + Unadjusted volume of the symbol. + change : float + Change in the price of the symbol from the previous day. + changePercent : float + Change \\% in the price of the symbol. + label : str + Human readable format of the date. + changeOverTime : float + Change \\% in the price of the symbol over a period of time. polygon ======= - Source: https://polygon.io/docs/forex/get_v2_aggs_ticker__forexticker__range__multiplier___timespan___from___to - Parameters ---------- - stocksTicker : str - The ticker symbol of the stocks to fetch. - start_date : Union[date, datetime], optional - The start date of the query. - end_date : Union[date, datetime], optional - The end date of the query. - timespan : Timespan, optional - The timespan of the query, by default Timespan.day - sort : Literal["asc", "desc"], optional - The sort order of the query, by default "desc" - limit : PositiveInt, optional - The limit of the query, by default 49999 - adjusted : bool, optional - Whether the query is adjusted, by default True - multiplier : PositiveInt, optional - The multiplier of the query, by default 1 + timespan : Literal['minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'] + The timespan of the data. + sort : Literal['asc', 'desc'] + Sort order of the data. + limit : PositiveInt + The number of data entries to return. + adjusted : bool + Whether the data is adjusted. + multiplier : PositiveInt + The multiplier of the timespan. - Returns - ------- - Documentation not available. - """ + ForexEOD + -------- + n : PositiveInt + The number of transactions for the symbol in the time period.""" inputs = filter_inputs( provider_choices={ "provider": provider, }, standard_params={ "symbol": symbol, + "start_date": start_date, + "end_date": end_date, }, extra_params=kwargs, chart=chart, diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/package/module_map.json b/openbb_sdk/sdk/core/openbb_core/app/static/package/module_map.json index ea685229edc..83224aab956 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/package/module_map.json +++ b/openbb_sdk/sdk/core/openbb_core/app/static/package/module_map.json @@ -1,6 +1,6 @@ { "crypto_load": "/crypto/load", - "__commands__": "", + "__extensions__": "", "crypto": "/crypto", "economy_corecpi": "/economy/corecpi", "economy": "/economy", @@ -76,8 +76,10 @@ "stocks": "/stocks", "stocks_fa": "/stocks/fa", "stocks_fa_balance": "/stocks/fa/balance", + "stocks_fa_balance_growth": "/stocks/fa/balance_growth", "stocks_fa_cal": "/stocks/fa/cal", "stocks_fa_cash": "/stocks/fa/cash", + "stocks_fa_cash_growth": "/stocks/fa/cash_growth", "stocks_fa_comp": "/stocks/fa/comp", "stocks_fa_comsplit": "/stocks/fa/comsplit", "stocks_fa_customer": "/stocks/fa/customer", @@ -95,7 +97,9 @@ "stocks_fa_growth": "/stocks/fa/growth", "stocks_fa_historical_5": "/stocks/fa/historical_5", "stocks_fa_income": "/stocks/fa/income", + "stocks_fa_income_growth": "/stocks/fa/income_growth", "stocks_fa_ins": "/stocks/fa/ins", + "stocks_fa_ins_own": "/stocks/fa/ins_own", "stocks_fa_key": "/stocks/fa/key", "stocks_fa_metrics": "/stocks/fa/metrics", "stocks_fa_mgmt": "/stocks/fa/mgmt", @@ -118,7 +122,7 @@ "stocks_fa_split": "/stocks/fa/split", "stocks_fa_supplier": "/stocks/fa/supplier", "stocks_fa_transcript": "/stocks/fa/transcript", - "stocks_ca_get": "/stocks/ca/get", + "stocks_ca_peers": "/stocks/ca/peers", "stocks_ca": "/stocks/ca", "stocks_ca_balance": "/stocks/ca/balance", "stocks_ca_cashflow": "/stocks/ca/cashflow", diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/package/news.py b/openbb_sdk/sdk/core/openbb_core/app/static/package/news.py index 6513d921a3a..f247525bb78 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/package/news.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/package/news.py @@ -25,19 +25,34 @@ class CLASS_news(Container): ) -> CommandOutput[typing.List]: """Global News. - Available providers: benzinga, fmp - Standard - ======== + openbb + ====== - Parameter - --------- + Parameters + ---------- + provider: Literal[benzinga, fmp] + The provider to use for the query. page : NonNegativeInt - The page of the global news to be retrieved. - + The page of the global news. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + GlobalNews + ---------- date : datetime The published date of the news. title : str @@ -52,14 +67,12 @@ class CLASS_news(Container): benzinga ======== - Source: https://docs.benzinga.io/benzinga/newsfeed-v2.html - - Parameter - --------- - pageSize : int (default: 15) + Parameters + ---------- + pageSize : int The number of results to return per page. - displayOutput : str (default: "headline") - The type of data to return. Options are "headline", "summary", "full", "all". + displayOutput : Literal['headline', 'summary', 'full', 'all'] + The type of data to return. date : Optional[datetime] The date of the news to retrieve. dateFrom : Optional[datetime] @@ -70,9 +83,8 @@ class CLASS_news(Container): The number of seconds since the news was updated. publishedSince : Optional[int] The number of seconds since the news was published. - sort : Optional[str] - The order in which to sort the news. Options are: - "published_at", "updated_at", "title", "author", "channel", "ticker", "topic", "content_type". + sort : Optional[Literal['published_at', 'updated_at', 'title', 'author', 'channel', 'ticker', 'topic', 'content_type']] + The order in which to sort the news. Options are: published_at, updated_at, title, author, channel, ticker, topic, content_type. isin : Optional[str] The ISIN of the news to retrieve. cusip : Optional[str] @@ -89,25 +101,22 @@ class CLASS_news(Container): The content types of the news to retrieve. - Returns - ------- - Documentation not available. - + GlobalNews + ---------- + All fields are standardized. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/general-news-api/ - - Parameter - --------- + Parameters + ---------- All fields are standardized. - Returns - ------- - Documentation not available. - """ + GlobalNews + ---------- + site : str + The site of the news.""" inputs = filter_inputs( provider_choices={ "provider": provider, diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks.py b/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks.py index b2be2bb758d..ab978460a0c 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks.py @@ -4,6 +4,7 @@ import datetime import typing from typing import Literal, Optional, Union +import pydantic from pydantic import validate_arguments import openbb_core.app.model.command_context @@ -91,78 +92,98 @@ class CLASS_stocks(Container): ) -> CommandOutput[typing.List]: """Load stock data for a specific ticker. - Available providers: fmp, polygon - Standard - ======== - Parameter - --------- - symbol : str - The symbol of the company. + openbb + ====== + + Parameters + ---------- + provider: Literal[fmp, polygon] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. start_date : Optional[date] - The start date of the stock data from which to retrieve the data. + Start date of the data, in YYYY-MM-DD format. end_date : Optional[date] - The end date of the stock data up to which to retrieve the data. - + End date of the data, in YYYY-MM-DD format. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + StockEOD + -------- date : datetime - The date of the stock. + The date of the data. open : PositiveFloat - The open price of the stock. + The open price of the symbol. high : PositiveFloat - The high price of the stock. + The high price of the symbol. low : PositiveFloat - The low price of the stock. + The low price of the symbol. close : PositiveFloat - The close price of the stock. - adj_close : Optional[PositiveFloat] - The adjusted close price of the stock. + The close price of the symbol. volume : PositiveFloat - The volume of the stock. + The volume of the symbol. + vwap : PositiveFloat + Volume Weighted Average Price of the symbol. fmp === - Source: https://financialmodelingprep.com/developer/docs/#Stock-Historical-Price - - Parameter - --------- - timeseries : Optional[int] - The number of days to look back. - series_type : Optional[Literal["line"]] - The type of the series. Only "line" is supported. + Parameters + ---------- + timeseries : Optional[NonNegativeInt] + Number of days to look back. - Returns - ------- - Documentation not available. - + StockEOD + -------- + adjClose : float + Adjusted Close Price of the symbol. + unadjustedVolume : float + Unadjusted volume of the symbol. + change : float + Change in the price of the symbol from the previous day. + changePercent : float + Change \\% in the price of the symbol. + label : str + Human readable format of the date. + changeOverTime : float + Change \\% in the price of the symbol over a period of time. polygon ======= - Source: https://polygon.io/docs/stocks/getting-started - Parameters ---------- - timespan : Timespan, optional - The timespan of the query, by default Timespan.day - sort : Literal["asc", "desc"], optional - The sort order of the query, by default "desc" - limit : PositiveInt, optional - The limit of the query, by default 49999 - adjusted : bool, optional - Whether the query is adjusted, by default True - multiplier : PositiveInt, optional - The multiplier of the query, by default 1 + timespan : Literal['minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'] + The timespan of the data. + sort : Literal['asc', 'desc'] + Sort order of the data. + limit : PositiveInt + The number of data entries to return. + adjusted : bool + Whether the data is adjusted. + multiplier : PositiveInt + The multiplier of the timespan. - Returns - ------- - Documentation not available. - """ + StockEOD + -------- + n : PositiveInt + The number of transactions for the symbol in the time period.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -189,35 +210,50 @@ class CLASS_stocks(Container): self, symbols: str, page: int = 0, + limit: Optional[pydantic.types.NonNegativeInt] = 15, chart: bool = False, provider: Optional[Literal["benzinga", "fmp", "polygon"]] = None, **kwargs ) -> CommandOutput[typing.List]: """Get news for one or more stock tickers. - Available providers: benzinga, fmp, polygon - Standard - ======== + openbb + ====== - - Parameter - --------- - symbols : str - The symbols of the company. + Parameters + ---------- + provider: Literal[benzinga, fmp, polygon] + The provider to use for the query. + symbols : ConstrainedStrValue + Symbol to get data for. page : int The page of the stock news to be retrieved. - + limit : Optional[NonNegativeInt] + The number of results to return per page. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + StockNews + --------- date : date The published date of the news. title : str The title of the news. - image : Optional[str] - The image URL of the news. - text : str + text : Optional[str] The text/body of the news. url : str The URL of the news. @@ -225,16 +261,10 @@ class CLASS_stocks(Container): benzinga ======== - Source: https://docs.benzinga.io/benzinga/newsfeed-v2.html - - Parameter - --------- - symbols: str - The symbols of the companies. - pageSize : int (default: 15) - The number of results to return per page. - headline : str - The type of data to return. Options are "headline", "summary", "full", "all". + Parameters + ---------- + displayOutput : Literal['headline', 'summary', 'full', 'all'] + The type of data to return. date : Optional[datetime] The date of the news to retrieve. dateFrom : Optional[datetime] @@ -245,9 +275,8 @@ class CLASS_stocks(Container): The number of seconds since the news was updated. publishedSince : Optional[int] The number of seconds since the news was published. - sort : Optional[str] - The order in which to sort the news. Options are: - "published_at", "updated_at", "title", "author", "channel", "ticker", "topic", "content_type". + sort : Optional[Literal['published_at', 'updated_at', 'title', 'author', 'channel', 'ticker', 'topic', 'content_type']] + The order in which to sort the news. Options are: published_at, updated_at, title, author, channel, ticker, topic, content_type. isin : Optional[str] The ISIN of the news to retrieve. cusip : Optional[str] @@ -262,56 +291,73 @@ class CLASS_stocks(Container): The content types of the news to retrieve. + StockNews + --------- + image : List[BenzingaImage] + The images associated with the news. + fmp === - Source: https://site.financialmodelingprep.com/developer/docs/stock-news-api/ + Parameters + ---------- + All fields are standardized. - Parameter + + StockNews --------- - limit : Optional[int] - The limit of the data to retrieve. - - - Returns - ------- - Documentation not available. - + symbol : str + Ticker of the fetched news. + image : Optional[str] + URL to the image of the news source. + site : str + Name of the news source. polygon ======= - Source: https://polygon.io/docs/stocks/get_v2_reference_news - Parameters ---------- - symbol : str - The symbol of the stocks to fetch. - ticker_lt : str, optional + ticker_lt : Optional[str] Less than, by default None - ticker_lte : str, optional + ticker_lte : Optional[str] Less than or equal, by default None - ticker_gt : str, optional + ticker_gt : Optional[str] Greater than, by default None - ticker_gte : str, optional + ticker_gte : Optional[str] Greater than or equal, by default None - published_utc : str, optional + published_utc : Optional[str] The published date of the query, by default None - published_utc_lt : str, optional + published_utc_lt : Optional[str] Less than, by default None - published_utc_lte : str, optional + published_utc_lte : Optional[str] Less than or equal, by default None - published_utc_gt : str, optional + published_utc_gt : Optional[str] Greater than, by default None - published_utc_gte : str, optional + published_utc_gte : Optional[str] Greater than or equal, by default None - order : Literal["asc", "desc"], optional + order : Optional[Literal['asc', 'desc']] The sort order of the query, by default None - limit : int, optional - The limit of the query, by default 100 - sort : str, optional + sort : Optional[str] The sort of the query, by default None - """ + + + StockNews + --------- + amp_url : Optional[str] + AMP URL. + author : Optional[str] + Author of the article. + id : str + Article ID. + image_url : Optional[str] + Image URL. + keywords : Optional[List[str]] + Keywords in the article + publisher : PolygonPublisher + Publisher of the article. + tickers : List[str] + Tickers covered in the article.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -319,6 +365,7 @@ class CLASS_stocks(Container): standard_params={ "symbols": symbols, "page": page, + "limit": limit, }, extra_params=kwargs, chart=chart, @@ -336,152 +383,169 @@ class CLASS_stocks(Container): def multiples( self, symbol: str, - limit: Optional[int] = None, + limit: Optional[int] = 100, chart: bool = False, provider: Optional[Literal["fmp"]] = None, **kwargs ) -> CommandOutput[typing.List]: """Get valuation multiples for a stock ticker. - Available providers: fmp, - Standard - ======== + openbb + ====== - Parameter - --------- - symbol : str - The symbol of the company. + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. limit : Optional[int] - The limit of the key metrics ttm to be returned. - + The number of data entries to return. Returns ------- - revenue_per_share_ttm: Optional[float] - The revenue per share of the stock calculated as trailing twelve months. - net_income_per_share_ttm: Optional[float] - The net income per share of the stock calculated as trailing twelve months. - operating_cash_flow_per_share_ttm: Optional[float] - The operating cash flow per share of the stock calculated as trailing twelve months. - free_cash_flow_per_share_ttm: Optional[float] - The free cash flow per share of the stock calculated as trailing twelve months. - cash_per_share_ttm: Optional[float] - The cash per share of the stock calculated as trailing twelve months. - book_value_per_share_ttm: Optional[float] - The book value per share of the stock calculated as trailing twelve months. - tangible_book_value_per_share_ttm: Optional[float] - The tangible book value per share of the stock calculated as trailing twelve months. - shareholders_equity_per_share_ttm: Optional[float] - The shareholders equity per share of the stock calculated as trailing twelve months. - interest_debt_per_share_ttm: Optional[float] - The interest debt per share of the stock calculated as trailing twelve months. - market_cap_ttm: Optional[float] - The market cap of the stock calculated as trailing twelve months. - enterprise_value_ttm: Optional[float] - The enterprise value of the stock calculated as trailing twelve months. - pe_ratio_ttm: Optional[float] - The PE ratio of the stock calculated as trailing twelve months. - price_to_sales_ratio_ttm: Optional[float] - The price to sales ratio of the stock calculated as trailing twelve months. - pocf_ratio_ttm: Optional[float] - The POCF ratio of the stock calculated as trailing twelve months. - pfcf_ratio_ttm: Optional[float] - The PFCF ratio of the stock calculated as trailing twelve months. - pb_ratio_ttm: Optional[float] - The PB ratio of the stock calculated as trailing twelve months. - ptb_ratio_ttm: Optional[float] - The PTB ratio of the stock calculated as trailing twelve months. - ev_to_sales_ttm: Optional[float] - The EV to sales of the stock calculated as trailing twelve months. - enterprise_value_over_ebitda_ttm: Optional[float] - The enterprise value over EBITDA of the stock calculated as trailing twelve months. - ev_to_operating_cash_flow_ttm: Optional[float] - The EV to operating cash flow of the stock calculated as trailing twelve months. - ev_to_free_cash_flow_ttm: Optional[float] - The EV to free cash flow of the stock calculated as trailing twelve months. - earnings_yield_ttm: Optional[float] - The earnings yield of the stock calculated as trailing twelve months. - free_cash_flow_yield_ttm: Optional[float] - The free cash flow yield of the stock calculated as trailing twelve months. - debt_to_equity_ttm: Optional[float] - The debt to equity of the stock calculated as trailing twelve months. - debt_to_assets_ttm: Optional[float] - The debt to assets of the stock calculated as trailing twelve months. - net_debt_to_ebitda_ttm: Optional[float] - The net debt to EBITDA of the stock calculated as trailing twelve months. - current_ratio_ttm: Optional[float] - The current ratio of the stock calculated as trailing twelve months. - interest_coverage_ttm: Optional[float] - The interest coverage of the stock calculated as trailing twelve months. - income_quality_ttm: Optional[float] - The income quality of the stock calculated as trailing twelve months. - dividend_yield_ttm: Optional[float] - The dividend yield of the stock calculated as trailing twelve months. - payout_ratio_ttm: Optional[float] - The payout ratio of the stock calculated as trailing twelve months. - sales_general_and_administrative_to_revenue_ttm: Optional[float] - The sales general and administrative to revenue of the stock calculated as trailing twelve months. - research_and_development_to_revenue_ttm: Optional[float] - The research and development to revenue of the stock calculated as trailing twelve months. - intangibles_to_total_assets_ttm: Optional[float] - The intangibles to total assets of the stock calculated as trailing twelve months. - capex_to_operating_cash_flow_ttm: Optional[float] - The capex to operating cash flow of the stock calculated as trailing twelve months. - capex_to_revenue_ttm: Optional[float] - The capex to revenue of the stock calculated as trailing twelve months. - capex_to_depreciation_ttm: Optional[float] - The capex to depreciation of the stock calculated as trailing twelve months. - stock_based_compensation_to_revenue_ttm: Optional[float] - The stock based compensation to revenue of the stock calculated as trailing twelve months. - graham_number_ttm: Optional[float] - The graham number of the stock calculated as trailing twelve months. - roic_ttm: Optional[float] - The ROIC of the stock calculated as trailing twelve months. - return_on_tangible_assets_ttm: Optional[float] - The return on tangible assets of the stock calculated as trailing twelve months. - graham_net_net_ttm: Optional[float] - The graham net net of the stock calculated as trailing twelve months. - working_capital_ttm: Optional[float] - The working capital of the stock calculated as trailing twelve months. - tangible_asset_value_ttm: Optional[float] - The tangible asset value of the stock calculated as trailing twelve months. - net_current_asset_value_ttm: Optional[float] - The net current asset value of the stock calculated as trailing twelve months. - invested_capital_ttm: Optional[float] - The invested capital of the stock calculated as trailing twelve months. - average_receivables_ttm: Optional[float] - The average receivables of the stock calculated as trailing twelve months. - average_payables_ttm: Optional[float] - The average payables of the stock calculated as trailing twelve months. - average_inventory_ttm: Optional[float] - The average inventory of the stock calculated as trailing twelve months. - days_sales_outstanding_ttm: Optional[float] - The days sales outstanding of the stock calculated as trailing twelve months. - days_payables_outstanding_ttm: Optional[float] - The days payables outstanding of the stock calculated as trailing twelve months. - days_of_inventory_on_hand_ttm: Optional[float] - The days of inventory on hand of the stock calculated as trailing twelve months. - receivables_turnover_ttm: Optional[float] - The receivables turnover of the stock calculated as trailing twelve months. - payables_turnover_ttm: Optional[float] - The payables turnover of the stock calculated as trailing twelve months. - inventory_turnover_ttm: Optional[float] - The inventory turnover of the stock calculated as trailing twelve months. - roe_ttm: Optional[float] - The ROE of the stock calculated as trailing twelve months. - capex_per_share_ttm: Optional[float] - The capex per share of the stock calculated as trailing twelve months. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + StockMultiples + -------------- + revenue_per_share_ttm : Optional[float] + Revenue per share calculated as trailing twelve months. + net_income_per_share_ttm : Optional[float] + Net income per share calculated as trailing twelve months. + operating_cash_flow_per_share_ttm : Optional[float] + Operating cash flow per share calculated as trailing twelve months. + free_cash_flow_per_share_ttm : Optional[float] + Free cash flow per share calculated as trailing twelve months. + cash_per_share_ttm : Optional[float] + Cash per share calculated as trailing twelve months. + book_value_per_share_ttm : Optional[float] + Book value per share calculated as trailing twelve months. + tangible_book_value_per_share_ttm : Optional[float] + Tangible book value per share calculated as trailing twelve months. + shareholders_equity_per_share_ttm : Optional[float] + Shareholders equity per share calculated as trailing twelve months. + interest_debt_per_share_ttm : Optional[float] + Interest debt per share calculated as trailing twelve months. + market_cap_ttm : Optional[float] + Market capitalization calculated as trailing twelve months. + enterprise_value_ttm : Optional[float] + Enterprise value calculated as trailing twelve months. + pe_ratio_ttm : Optional[float] + Price-to-earnings ratio (P/E ratio) calculated as trailing twelve months. + price_to_sales_ratio_ttm : Optional[float] + Price-to-sales ratio calculated as trailing twelve months. + pocf_ratio_ttm : Optional[float] + Price-to-operating cash flow ratio calculated as trailing twelve months. + pfcf_ratio_ttm : Optional[float] + Price-to-free cash flow ratio calculated as trailing twelve months. + pb_ratio_ttm : Optional[float] + Price-to-book ratio calculated as trailing twelve months. + ptb_ratio_ttm : Optional[float] + Price-to-tangible book ratio calculated as trailing twelve months. + ev_to_sales_ttm : Optional[float] + Enterprise value-to-sales ratio calculated as trailing twelve months. + enterprise_value_over_ebitda_ttm : Optional[float] + Enterprise value-to-EBITDA ratio calculated as trailing twelve months. + ev_to_operating_cash_flow_ttm : Optional[float] + Enterprise value-to-operating cash flow ratio calculated as trailing twelve months. + ev_to_free_cash_flow_ttm : Optional[float] + Enterprise value-to-free cash flow ratio calculated as trailing twelve months. + earnings_yield_ttm : Optional[float] + Earnings yield calculated as trailing twelve months. + free_cash_flow_yield_ttm : Optional[float] + Free cash flow yield calculated as trailing twelve months. + debt_to_equity_ttm : Optional[float] + Debt-to-equity ratio calculated as trailing twelve months. + debt_to_assets_ttm : Optional[float] + Debt-to-assets ratio calculated as trailing twelve months. + net_debt_to_ebitda_ttm : Optional[float] + Net debt-to-EBITDA ratio calculated as trailing twelve months. + current_ratio_ttm : Optional[float] + Current ratio calculated as trailing twelve months. + interest_coverage_ttm : Optional[float] + Interest coverage calculated as trailing twelve months. + income_quality_ttm : Optional[float] + Income quality calculated as trailing twelve months. + dividend_yield_ttm : Optional[float] + Dividend yield calculated as trailing twelve months. + payout_ratio_ttm : Optional[float] + Payout ratio calculated as trailing twelve months. + sales_general_and_administrative_to_revenue_ttm : Optional[float] + Sales general and administrative expenses-to-revenue ratio calculated as trailing twelve months. + research_and_development_to_revenue_ttm : Optional[float] + Research and development expenses-to-revenue ratio calculated as trailing twelve months. + intangibles_to_total_assets_ttm : Optional[float] + Intangibles-to-total assets ratio calculated as trailing twelve months. + capex_to_operating_cash_flow_ttm : Optional[float] + Capital expenditures-to-operating cash flow ratio calculated as trailing twelve months. + capex_to_revenue_ttm : Optional[float] + Capital expenditures-to-revenue ratio calculated as trailing twelve months. + capex_to_depreciation_ttm : Optional[float] + Capital expenditures-to-depreciation ratio calculated as trailing twelve months. + stock_based_compensation_to_revenue_ttm : Optional[float] + Stock-based compensation-to-revenue ratio calculated as trailing twelve months. + graham_number_ttm : Optional[float] + Graham number calculated as trailing twelve months. + roic_ttm : Optional[float] + Return on invested capital calculated as trailing twelve months. + return_on_tangible_assets_ttm : Optional[float] + Return on tangible assets calculated as trailing twelve months. + graham_net_net_ttm : Optional[float] + Graham net-net working capital calculated as trailing twelve months. + working_capital_ttm : Optional[float] + Working capital calculated as trailing twelve months. + tangible_asset_value_ttm : Optional[float] + Tangible asset value calculated as trailing twelve months. + net_current_asset_value_ttm : Optional[float] + Net current asset value calculated as trailing twelve months. + invested_capital_ttm : Optional[float] + Invested capital calculated as trailing twelve months. + average_receivables_ttm : Optional[float] + Average receivables calculated as trailing twelve months. + average_payables_ttm : Optional[float] + Average payables calculated as trailing twelve months. + average_inventory_ttm : Optional[float] + Average inventory calculated as trailing twelve months. + days_sales_outstanding_ttm : Optional[float] + Days sales outstanding calculated as trailing twelve months. + days_payables_outstanding_ttm : Optional[float] + Days payables outstanding calculated as trailing twelve months. + days_of_inventory_on_hand_ttm : Optional[float] + Days of inventory on hand calculated as trailing twelve months. + receivables_turnover_ttm : Optional[float] + Receivables turnover calculated as trailing twelve months. + payables_turnover_ttm : Optional[float] + Payables turnover calculated as trailing twelve months. + inventory_turnover_ttm : Optional[float] + Inventory turnover calculated as trailing twelve months. + roe_ttm : Optional[float] + Return on equity calculated as trailing twelve months. + capex_per_share_ttm : Optional[float] + Capital expenditures per share calculated as trailing twelve months. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/#Company-Key-Metrics - - Parameter - --------- + Parameters + ---------- All fields are standardized. - """ + + + StockMultiples + -------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks_ca.py b/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks_ca.py index 3f7d604c51b..8cd9d795b71 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks_ca.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks_ca.py @@ -15,7 +15,7 @@ from openbb_core.app.static.filters import filter_call, filter_inputs, filter_ou class CLASS_stocks_ca(Container): @filter_call @validate_arguments - def get( + def peers( self, symbol: str, chart: bool = False, @@ -24,33 +24,50 @@ class CLASS_stocks_ca(Container): ) -> CommandOutput[typing.List]: """Company peers. - Available providers: fmp, - Standard - ======== - - Parameter - --------- - symbol: str - The symbol of the company. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + StockPeers + ---------- symbol : str - The symbol of the company to retrieve the stock peers of. - peers_list : List[str] - A list of stock peers based on sector, exchange and market cap + Symbol representing the entity requested in the data. + peers_list : Optional[List[str]] + A list of stock peers based on sector, exchange and market cap. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/#Stock-Peers - - Parameter - --------- + Parameters + ---------- All fields are standardized. - """ + + + StockPeers + ---------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -63,7 +80,7 @@ class CLASS_stocks_ca(Container): ) o = self._command_runner_session.run( - "/stocks/ca/get", + "/stocks/ca/peers", **inputs, ).output diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks_dd.py b/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks_dd.py index 2a7e32659d4..71ff5dea390 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks_dd.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks_dd.py @@ -16,38 +16,155 @@ class CLASS_stocks_dd(Container): def sec( self, symbol: str, - type: Optional[str] = None, + type: Literal[ + "1", + "1-A", + "1-E", + "1-K", + "1-N", + "1-SA", + "1-U", + "1-Z", + "10", + "10-D", + "10-K", + "10-M", + "10-Q", + "11-K", + "12b-25", + "13F", + "13H", + "144", + "15", + "15F", + "17-H", + "18", + "18-K", + "19b-4", + "19b-4(e)", + "19b-7", + "2-E", + "20-F", + "24F-2", + "25", + "3", + "4", + "40-F", + "5", + "6-K", + "7-M", + "8-A", + "8-K", + "8-M", + "9-M", + "ABS-15G", + "ABS-EE", + "ABS DD-15E", + "ADV", + "ADV-E", + "ADV-H", + "ADV-NR", + "ADV-W", + "ATS", + "ATS-N", + "ATS-R", + "BD", + "BD-N", + "BDW", + "C", + "CA-1", + "CB", + "CFPORTAL", + "CRS", + "CUSTODY", + "D", + "F-1", + "F-10", + "F-3", + "F-4", + "F-6", + "F-7", + "F-8", + "F-80", + "F-N", + "F-X", + "ID", + "MA", + "MA-I", + "MA-NR", + "MA-W", + "MSD", + "MSDW", + "N-14", + "N-17D-1", + "N-17f-1", + "N-17f-2", + "N-18f-1", + "N-1A", + "N-2", + "N-23c-3", + "N-27D-1", + "N-3", + "N-4", + "N-5", + "N-54A", + "N-54C", + "N-6", + "N-6EI-1", + "N-6F", + "N-8A", + "N-8B-2", + "N-8B-4", + "N-8F", + "N-CEN", + ] = "10-K", page: Optional[int] = 0, - limit: Optional[int] = None, + limit: Optional[int] = 100, chart: bool = False, provider: Optional[Literal["fmp"]] = None, **kwargs ) -> CommandOutput[typing.List]: """SEC Filings. - Available providers: fmp, - Standard - ======== - Parameter - --------- - symbol : str - The symbol of the company. - type : str - The type of the SEC filing form. (full list: https://www.sec.gov/forms) - page : int - The page of the results. - limit : int - The limit of the results. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + type : Literal['1', '1-A', '1-E', '1-K', '1-N', '1-SA', '1-U', '1-Z', '10', '10-D', '10-K', '10-M', '10-Q', '11-K', '12b-25', '13F', '13H', '144', '15', '15F', '17-H', '18', '18-K', '19b-4', '19b-4(e)', '19b-7', '2-E', '20-F', '24F-2', '25', '3', '4', '40-F', '5', '6-K', '7-M', '8-A', '8-K', '8-M', '9-M', 'ABS-15G', 'ABS-EE', 'ABS DD-15E', 'ADV', 'ADV-E', 'ADV-H', 'ADV-NR', 'ADV-W', 'ATS', 'ATS-N', 'ATS-R', 'BD', 'BD-N', 'BDW', 'C', 'CA-1', 'CB', 'CFPORTAL', 'CRS', 'CUSTODY', 'D', 'F-1', 'F-10', 'F-3', 'F-4', 'F-6', 'F-7', 'F-8', 'F-80', 'F-N', 'F-X', 'ID', 'MA', 'MA-I', 'MA-NR', 'MA-W', 'MSD', 'MSDW', 'N-14', 'N-17D-1', 'N-17f-1', 'N-17f-2', 'N-18f-1', 'N-1A', 'N-2', 'N-23c-3', 'N-27D-1', 'N-3', 'N-4', 'N-5', 'N-54A', 'N-54C', 'N-6', 'N-6EI-1', 'N-6F', 'N-8A', 'N-8B-2', 'N-8B-4', 'N-8F', 'N-CEN'] + The type of the SEC filing form. + page : Optional[int] + Page number of the results. + limit : Optional[int] + The number of data entries to return. Returns ------- - symbol : str - The symbol of the stock. - filling_date : date + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + SECFilings + ---------- + symbol : ConstrainedStrValue + Symbol to get data for. + filling_date : datetime The filling date of the SEC filing. - accepted_date : date + accepted_date : datetime The accepted date of the SEC filing. cik : str The CIK of the SEC filing. @@ -61,16 +178,14 @@ class CLASS_stocks_dd(Container): fmp === - Source: https://site.financialmodelingprep.com/developer/docs/sec-filings-api/ + Parameters + ---------- + All fields are standardized. - Parameter - --------- - The type of the SEC filing form. (full list: https://www.sec.gov/forms) - Returns - ------- - Documentation not available. - """ + SECFilings + ---------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks_fa.py b/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks_fa.py index f7fd3c64475..c005d43ffd0 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks_fa.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/package/stocks_fa.py @@ -4,7 +4,7 @@ import datetime import typing from typing import List, Literal, Optional, Union -import openbb_provider +import pydantic from pydantic import validate_arguments import openbb_core.app.model.command_context @@ -38,109 +38,173 @@ class CLASS_stocks_fa(Container): self, symbol: str, period: Literal["annually", "quarterly"] = "annually", + limit: Optional[pydantic.types.NonNegativeInt] = 12, chart: bool = False, provider: Optional[Literal["fmp", "polygon"]] = None, **kwargs ) -> CommandOutput[typing.List]: """Balance Sheet. - Available providers: fmp, polygon - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the company. - period : Literal["annually", "quarterly"] - The period of the balance sheet. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp, polygon] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + period : Literal['annually', 'quarterly'] + Period of the data to return (quarterly or annually). + limit : Optional[NonNegativeInt] + The number of data entries to return. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + BalanceSheet + ------------ date : date - The date of the balance sheet. + Date of the fetched statement. + symbol : Optional[str] + Symbol of the company. cik : Optional[int] - The CIK of the company. + Central Index Key. + currency : Optional[str] + Reporting currency. + filing_date : Optional[date] + Filling date. + accepted_date : Optional[datetime] + Accepted date. + period : Optional[str] + Reporting period of the statement. cash_and_cash_equivalents : Optional[int] - The cash and cash equivalents of the balance sheet. + Cash and cash equivalents short_term_investments : Optional[int] - The short term investments of the balance sheet. - cash_and_short_term_investments : Optional[int] - The cash and short term investments of the balance sheet. - net_receivables : Optional[int] - The net receivables of the balance sheet. + Short-term investments inventory : Optional[int] - The inventory of the balance sheet. + Inventory + net_receivables : Optional[int] + Receivables, net other_current_assets : Optional[int] - The other current assets of the balance sheet. + Other current assets current_assets : Optional[int] - The current assets of the balance sheet. + Total current assets long_term_investments : Optional[int] - The long term investments of the balance sheet. + Long-term investments property_plant_equipment_net : Optional[int] - The property plant equipment net of the balance sheet. + Property, plant and equipment, net + goodwill : Optional[int] + Goodwill + intangible_assets : Optional[int] + Intangible assets other_non_current_assets : Optional[int] - The other non current assets of the balance sheet. + Other non-current assets + tax_assets : Optional[int] + Accrued income taxes + other_assets : Optional[int] + Other assets noncurrent_assets : Optional[int] - The non current assets of the balance sheet. + None assets : Optional[int] - The assets of the balance sheet. - accounts_payable : Optional[int] - The accounts payable of the balance sheet. + None + account_payables : Optional[int] + None other_current_liabilities : Optional[int] - The other current liabilities of the balance sheet. + None + tax_payables : Optional[int] + Accrued income taxes deferred_revenue : Optional[int] - The deferred revenue of the balance sheet. + Accrued income taxes, other deferred revenue + short_term_debt : Optional[int] + Short-term borrowings, Long-term debt due within one year, Operating lease obligations due within one year, Finance lease obligations due within one year current_liabilities : Optional[int] - The current liabilities of the balance sheet. + None long_term_debt : Optional[int] - The long term debt of the balance sheet. + Long-term debt, Operating lease obligations, Long-term finance lease obligations other_non_current_liabilities : Optional[int] - The other non current liabilities of the balance sheet. + Deferred income taxes and other + other_liabilities : Optional[int] + None noncurrent_liabilities : Optional[int] - The non current liabilities of the balance sheet. + None liabilities : Optional[int] - The liabilities of the balance sheet. + None common_stock : Optional[int] - The common stock of the balance sheet. - retained_earnings : Optional[int] - The retained earnings of the balance sheet. + None + other_stockholder_equity : Optional[int] + Capital in excess of par value accumulated_other_comprehensive_income_loss : Optional[int] - The accumulated other comprehensive income loss of the balance sheet. + Accumulated other comprehensive income (loss) + preferred_stock : Optional[int] + Preferred stock + retained_earnings : Optional[int] + Retained earnings + minority_interest : Optional[int] + Minority interest + total_stockholders_equity : Optional[int] + None total_equity : Optional[int] - The total equity of the balance sheet. + None total_liabilities_and_stockholders_equity : Optional[int] - The total liabilities and stockholders equity of the balance sheet. + None + total_liabilities_and_total_equity : Optional[int] + None fmp === - Source: https://financialmodelingprep.com/developer/docs/#Balance-Sheet - - Parameter - --------- + Parameters + ---------- cik : Optional[str] - The CIK of the company if symbol is not provided. - limit : Optional[NonNegativeInt] - The limit of the balance sheet. + None - Returns - ------- - Documentation not available. - + BalanceSheet + ------------ + calendarYear : Optional[int] + None + link : Optional[str] + None + finalLink : Optional[str] + None + cashAndShortTermInvestments : Optional[int] + None + goodwillAndIntangibleAssets : Optional[int] + None + deferredRevenueNonCurrent : Optional[int] + None + totalInvestments : Optional[int] + None + capitalLeaseObligations : Optional[int] + None + deferredTaxLiabilitiesNonCurrent : Optional[int] + None + totalNonCurrentLiabilities : Optional[int] + None + totalDebt : Optional[int] + None + netDebt : Optional[int] + None polygon ======= - Source: https://polygon.io/docs/stocks#!/get_vx_reference_financials - - Parameter - --------- - cik : Optional[str] - The CIK of the company if no symbol is provided. + Parameters + ---------- company_name : Optional[str] The name of the company. company_name_search : Optional[str] @@ -167,22 +231,17 @@ class CLASS_stocks_fa(Container): The period of report date greater than the given date. period_of_report_date_gte : Optional[date] The period of report date greater than or equal to the given date. - timeframe : Optional[Literal["annual", "quarterly", "ttm"]] - The timeframe of the financial statement. include_sources : Optional[bool] Whether to include the sources of the financial statement. - order : Optional[Literal["asc", "desc"]] + order : Optional[Literal['asc', 'desc']] The order of the financial statement. - limit : Optional[PositiveInt] - The limit of the financial statement. - sort : Optional[Literal["filing_date", "period_of_report_date"]] + sort : Optional[Literal['filing_date', 'period_of_report_date']] The sort of the financial statement. - Returns - ------- - Documentation not available. - """ + BalanceSheet + ------------ + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -190,6 +249,7 @@ class CLASS_stocks_fa(Container): standard_params={ "symbol": symbol, "period": period, + "limit": limit, }, extra_params=kwargs, chart=chart, @@ -202,67 +262,233 @@ class CLASS_stocks_fa(Container): return filter_output(o) + @filter_call + @validate_arguments + def balance_growth( + self, + symbol: str, + limit: int = 10, + chart: bool = False, + provider: Optional[Literal["fmp"]] = None, + **kwargs + ) -> CommandOutput[typing.List]: + """Balance Sheet Statement Growth. + + + openbb + ====== + + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + limit : int + The number of data entries to return. + + Returns + ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + BalanceSheetGrowth + ------------------ + symbol : ConstrainedStrValue + Symbol to get data for. + date : date + The date of the data. + period : str + The period the statement is returned for. + growth_cash_and_cash_equivalents : float + Growth rate of cash and cash equivalents. + growth_short_term_investments : float + Growth rate of short-term investments. + growth_cash_and_short_term_investments : float + Growth rate of cash and short-term investments. + growth_net_receivables : float + Growth rate of net receivables. + growth_inventory : float + Growth rate of inventory. + growth_other_current_assets : float + Growth rate of other current assets. + growth_total_current_assets : float + Growth rate of total current assets. + growth_property_plant_equipment_net : float + Growth rate of net property, plant, and equipment. + growth_goodwill : float + Growth rate of goodwill. + growth_intangible_assets : float + Growth rate of intangible assets. + growth_goodwill_and_intangible_assets : float + Growth rate of goodwill and intangible assets. + growth_long_term_investments : float + Growth rate of long-term investments. + growth_tax_assets : float + Growth rate of tax assets. + growth_other_non_current_assets : float + Growth rate of other non-current assets. + growth_total_non_current_assets : float + Growth rate of total non-current assets. + growth_other_assets : float + Growth rate of other assets. + growth_total_assets : float + Growth rate of total assets. + growth_account_payables : float + Growth rate of accounts payable. + growth_short_term_debt : float + Growth rate of short-term debt. + growth_tax_payables : float + Growth rate of tax payables. + growth_deferred_revenue : float + Growth rate of deferred revenue. + growth_other_current_liabilities : float + Growth rate of other current liabilities. + growth_total_current_liabilities : float + Growth rate of total current liabilities. + growth_long_term_debt : float + Growth rate of long-term debt. + growth_deferred_revenue_non_current : float + Growth rate of non-current deferred revenue. + growth_deferrred_tax_liabilities_non_current : float + Growth rate of non-current deferred tax liabilities. + growth_other_non_current_liabilities : float + Growth rate of other non-current liabilities. + growth_total_non_current_liabilities : float + Growth rate of total non-current liabilities. + growth_other_liabilities : float + Growth rate of other liabilities. + growth_total_liabilities : float + Growth rate of total liabilities. + growth_common_stock : float + Growth rate of common stock. + growth_retained_earnings : float + Growth rate of retained earnings. + growth_accumulated_other_comprehensive_income_loss : float + Growth rate of accumulated other comprehensive income/loss. + growth_othertotal_stockholders_equity : float + Growth rate of other total stockholders' equity. + growth_total_stockholders_equity : float + Growth rate of total stockholders' equity. + growth_total_liabilities_and_stockholders_equity : float + Growth rate of total liabilities and stockholders' equity. + growth_total_investments : float + Growth rate of total investments. + growth_total_debt : float + Growth rate of total debt. + growth_net_debt : float + Growth rate of net debt. + + fmp + === + + Parameters + ---------- + All fields are standardized. + + + BalanceSheetGrowth + ------------------ + All fields are standardized.""" + inputs = filter_inputs( + provider_choices={ + "provider": provider, + }, + standard_params={ + "symbol": symbol, + "limit": limit, + }, + extra_params=kwargs, + chart=chart, + ) + + o = self._command_runner_session.run( + "/stocks/fa/balance_growth", + **inputs, + ).output + + return filter_output(o) + @filter_call @validate_arguments def cal( self, - start_date: Union[datetime.date, None, str] = datetime.date(2023, 6, 23), - end_date: Union[datetime.date, None, str] = datetime.date(2023, 7, 23), + start_date: Union[datetime.date, None, str] = None, + end_date: Union[datetime.date, None, str] = None, chart: bool = False, provider: Optional[Literal["fmp"]] = None, **kwargs ) -> CommandOutput[typing.List]: """Show Dividend Calendar for a given start and end dates. - Available providers: fmp, - Standard - ======== - - The maximum time interval between the start and end date can be 3 months. - Default value for time interval is 1 month. - - Parameter - --------- - start_date : date - The starting date to fetch the dividend calendar from. Default value is the - previous day from the last month. - end_date : date - The ending date to fetch the dividend calendar till. Default value is the - previous day from the current month. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + start_date : Optional[date] + Start date of the data, in YYYY-MM-DD format. + end_date : Optional[date] + End date of the data, in YYYY-MM-DD format. Returns ------- - date : dateType - The date of the dividend in the calendar. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + DividendCalendar + ---------------- + symbol : ConstrainedStrValue + Symbol to get data for. + date : date + The date of the data. label : str The date in human readable form in the calendar. - adjDividend : Optional[NonNegativeFloat] + adj_dividend : Optional[NonNegativeFloat] The adjusted dividend on a date in the calendar. - symbol : str - The symbol of the company for which the dividend is returned in the calendar. dividend : Optional[NonNegativeFloat] The dividend amount in the calendar. - recordDate : Optional[dateType] + record_date : Optional[date] The record date of the dividend in the calendar. - paymentDate : Optional[dateType] + payment_date : Optional[date] The payment date of the dividend in the calendar. - declarationDate : Optional[dateType] + declaration_date : Optional[date] The declaration date of the dividend in the calendar. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/dividend-calendar-api/ - - The maximum time interval between the start and end date can be 3 months. - Default value for time interval is 1 month. - - Parameter - --------- + Parameters + ---------- All fields are standardized. - """ + + + DividendCalendar + ---------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -288,103 +514,135 @@ class CLASS_stocks_fa(Container): self, symbol: str, period: Literal["annually", "quarterly"] = "annually", + limit: Optional[pydantic.types.NonNegativeInt] = 12, chart: bool = False, provider: Optional[Literal["fmp", "polygon"]] = None, **kwargs ) -> CommandOutput[typing.List]: """Cash Flow Statement. - Available providers: fmp, polygon - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the company. - period : Literal["annually", "quarterly"] - The period of the income statement. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp, polygon] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + period : Literal['annually', 'quarterly'] + Period of the data to return (quarterly or annually). + limit : Optional[NonNegativeInt] + The number of data entries to return. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + CashFlowStatement + ----------------- date : date - The date of the cash flow statement. - symbol : str - The symbol of the company. - currency : Optional[str] - The currency of the cash flow statement. + Date of the fetched statement. + symbol : Optional[str] + Symbol of the company. cik : Optional[int] - The Central Index Key (CIK) of the company. + Central Index Key. + currency : Optional[str] + Reporting currency. + filing_date : Optional[date] + Filling date. + accepted_date : Optional[datetime] + Accepted date. period : Optional[str] - The period of the cash flow statement. + Reporting period of the statement. cash_at_beginning_of_period : Optional[int] - The cash at the beginning of the period. + None net_income : Optional[int] - The net income. + None depreciation_and_amortization : Optional[int] - The depreciation and amortization. + None stock_based_compensation : Optional[int] - The stock based compensation. - accounts_receivables : Optional[int] - The accounts receivables. + None + other_non_cash_items : Optional[int] + Other non-cash items. + deferred_income_tax : Optional[int] + Deferred income tax. inventory : Optional[int] - The inventory. + None + accounts_receivables : Optional[int] + None accounts_payables : Optional[int] - The accounts payables. + None + other_working_capital : Optional[int] + Accrued expenses and other, Unearned revenue. net_cash_flow_from_operating_activities : Optional[int] - The net cash flow from operating activities. - purchases_of_investments : Optional[int] - The purchases of investments. + None + capital_expenditure : Optional[int] + Purchases of property and equipment. + other_investing_activities : Optional[int] + Proceeds from property and equipment sales and incentives. + acquisitions_net : Optional[int] + Acquisitions, net of cash acquired, and other sales_maturities_of_investments : Optional[int] - The sales maturities of investments. - investments_in_property_plant_and_equipment : Optional[int] - The investments in property plant and equipment. + None + purchases_of_investments : Optional[int] + None net_cash_flow_from_investing_activities : Optional[int] - The net cash flow from investing activities. + None + investments_in_property_plant_and_equipment : Optional[int] + None dividends_paid : Optional[int] - The dividends paid. + None common_stock_repurchased : Optional[int] - The common stock repurchased. + None debt_repayment : Optional[int] - The debt repayment. + None other_financing_activities : Optional[int] - The other financing activities. + None net_cash_flow_from_financing_activities : Optional[int] - The net cash flow from financing activities. - net_cash_flow : Optional[int] - The net cash flow. + None + effect_of_forex_changes_on_cash : Optional[int] + Foreign currency effect on cash, cash equivalents, and restricted cash + net_change_in_cash : Optional[int] + Net increase (decrease) in cash, cash equivalents, and restricted cash cash_at_end_of_period : Optional[int] - The cash at the end of the period. + None fmp === - Source: https://financialmodelingprep.com/developer/docs/#Cash-Flow-Statement - - Parameter - --------- + Parameters + ---------- cik : Optional[str] - The CIK of the company if symbol is not provided. - limit : Optional[NonNegativeInt] - The limit of the cash flow statement. + Central Index Key (CIK) of the company. - Returns - ------- - Documentation not available. - + CashFlowStatement + ----------------- + calendarYear : Optional[int] + None + link : Optional[str] + None + finalLink : Optional[str] + None polygon ======= - Source: https://polygon.io/docs/stocks#!/get_vx_reference_financials - - Parameter - --------- - cik : Optional[str] - The CIK of the company if no symbol is provided. + Parameters + ---------- company_name : Optional[str] The name of the company. company_name_search : Optional[str] @@ -411,22 +669,17 @@ class CLASS_stocks_fa(Container): The period of report date greater than the given date. period_of_report_date_gte : Optional[date] The period of report date greater than or equal to the given date. - timeframe : Optional[Literal["annual", "quarterly", "ttm"]] - The timeframe of the financial statement. include_sources : Optional[bool] Whether to include the sources of the financial statement. - order : Optional[Literal["asc", "desc"]] + order : Optional[Literal['asc', 'desc']] The order of the financial statement. - limit : Optional[PositiveInt] - The limit of the financial statement. - sort : Optional[Literal["filing_date", "period_of_report_date"]] + sort : Optional[Literal['filing_date', 'period_of_report_date']] The sort of the financial statement. - Returns - ------- - Documentation not available. - """ + CashFlowStatement + ----------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -434,6 +687,7 @@ class CLASS_stocks_fa(Container): standard_params={ "symbol": symbol, "period": period, + "limit": limit, }, extra_params=kwargs, chart=chart, @@ -446,6 +700,145 @@ class CLASS_stocks_fa(Container): return filter_output(o) + @filter_call + @validate_arguments + def cash_growth( + self, + symbol: str, + limit: int = 10, + chart: bool = False, + provider: Optional[Literal["fmp"]] = None, + **kwargs + ) -> CommandOutput[typing.List]: + """Cash Flow Statement Growth. + + + openbb + ====== + + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + limit : int + The number of data entries to return. + + Returns + ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + CashFlowStatementGrowth + ----------------------- + symbol : ConstrainedStrValue + Symbol to get data for. + date : date + The date of the data. + period : str + The period the statement is returned for. + growth_net_income : float + Growth rate of net income. + growth_depreciation_and_amortization : float + Growth rate of depreciation and amortization. + growth_deferred_income_tax : float + Growth rate of deferred income tax. + growth_stock_based_compensation : float + Growth rate of stock-based compensation. + growth_change_in_working_capital : float + Growth rate of change in working capital. + growth_accounts_receivables : float + Growth rate of accounts receivables. + growth_inventory : float + Growth rate of inventory. + growth_accounts_payables : float + Growth rate of accounts payables. + growth_other_working_capital : float + Growth rate of other working capital. + growth_other_non_cash_items : float + Growth rate of other non-cash items. + growth_net_cash_provided_by_operating_activities : float + Growth rate of net cash provided by operating activities. + growth_investments_in_property_plant_and_equipment : float + Growth rate of investments in property, plant, and equipment. + growth_acquisitions_net : float + Growth rate of net acquisitions. + growth_purchases_of_investments : float + Growth rate of purchases of investments. + growth_sales_maturities_of_investments : float + Growth rate of sales maturities of investments. + growth_other_investing_activities : float + Growth rate of other investing activities. + growth_net_cash_used_for_investing_activities : float + Growth rate of net cash used for investing activities. + growth_debt_repayment : float + Growth rate of debt repayment. + growth_common_stock_issued : float + Growth rate of common stock issued. + growth_common_stock_repurchased : float + Growth rate of common stock repurchased. + growth_dividends_paid : float + Growth rate of dividends paid. + growth_other_financing_activities : float + Growth rate of other financing activities. + growth_net_cash_used_provided_by_financing_activities : float + Growth rate of net cash used/provided by financing activities. + growth_effect_of_forex_changes_on_cash : float + Growth rate of the effect of foreign exchange changes on cash. + growth_net_change_in_cash : float + Growth rate of net change in cash. + growth_cash_at_end_of_period : float + Growth rate of cash at the end of the period. + growth_cash_at_beginning_of_period : float + Growth rate of cash at the beginning of the period. + growth_operating_cash_flow : float + Growth rate of operating cash flow. + growth_capital_expenditure : float + Growth rate of capital expenditure. + growth_free_cash_flow : float + Growth rate of free cash flow. + + fmp + === + + Parameters + ---------- + All fields are standardized. + + + CashFlowStatementGrowth + ----------------------- + All fields are standardized.""" + inputs = filter_inputs( + provider_choices={ + "provider": provider, + }, + standard_params={ + "symbol": symbol, + "limit": limit, + }, + extra_params=kwargs, + chart=chart, + ) + + o = self._command_runner_session.run( + "/stocks/fa/cash_growth", + **inputs, + ).output + + return filter_output(o) + @filter_call @validate_arguments def comp( @@ -457,60 +850,72 @@ class CLASS_stocks_fa(Container): ) -> CommandOutput[typing.List]: """Executive Compensation. - Available providers: fmp, - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the company. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + ExecutiveCompensation + --------------------- + symbol : ConstrainedStrValue + Symbol to get data for. cik : Optional[str] The Central Index Key (CIK) of the company. - symbol : str - The symbol of the company. - filing_date : dateType + filing_date : date The date of the filing. - accepted_date : dateType + accepted_date : datetime The date the filing was accepted. name_and_position : str The name and position of the executive. year : int The year of the compensation. - salary : float + salary : PositiveFloat The salary of the executive. - bonus : float + bonus : NonNegativeFloat The bonus of the executive. - stock_award : float + stock_award : NonNegativeFloat The stock award of the executive. - incentive_plan_compensation : float + incentive_plan_compensation : NonNegativeFloat The incentive plan compensation of the executive. - all_other_compensation : float + all_other_compensation : NonNegativeFloat The all other compensation of the executive. - total : float + total : PositiveFloat The total compensation of the executive. url : str - The URL of the filing. + The URL of the filing data. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/executive-compensation-api/ - - Parameter - --------- + Parameters + ---------- All fields are standardized. - Returns - ------- - Documentation not available. - """ + ExecutiveCompensation + --------------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -541,21 +946,36 @@ class CLASS_stocks_fa(Container): ) -> CommandOutput[typing.List]: """Stock Split Calendar. - Available providers: fmp, - Standard - ======== - - Parameter - --------- - start_date : date - The start date of the stock splits from which to retrieve the data. - end_date : date - The end date of the stock splits up to which to retrieve the data. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + start_date : Optional[date] + Start date of the data, in YYYY-MM-DD format. + end_date : Optional[date] + End date of the data, in YYYY-MM-DD format. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + StockSplitCalendar + ------------------ date : date The date of the stock splits. label : str @@ -570,12 +990,14 @@ class CLASS_stocks_fa(Container): fmp === - Source: https://site.financialmodelingprep.com/developer/docs/stock-split-calendar-api/ - - Parameter - --------- + Parameters + ---------- All fields are standardized. - """ + + + StockSplitCalendar + ------------------ + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -640,19 +1062,34 @@ class CLASS_stocks_fa(Container): ) -> CommandOutput[typing.List]: """Historical Dividends. - Available providers: fmp, - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the company. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + HistoricalDividends + ------------------- date : date The date of the historical dividends. label : str @@ -661,22 +1098,24 @@ class CLASS_stocks_fa(Container): The adjusted dividend of the historical dividends. dividend : float The dividend of the historical dividends. - record_date : date + record_date : Optional[date] The record date of the historical dividends. - payment_date : date + payment_date : Optional[date] The payment date of the historical dividends. - declaration_date : date + declaration_date : Optional[date] The declaration date of the historical dividends. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/#Historical-Dividends - - Parameter - --------- + Parameters + ---------- All fields are standardized. - """ + + + HistoricalDividends + ------------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -717,42 +1156,58 @@ class CLASS_stocks_fa(Container): def earning( self, symbol: str, - limit: Optional[int] = None, + limit: Optional[int] = 50, chart: bool = False, provider: Optional[Literal["fmp"]] = None, **kwargs ) -> CommandOutput[typing.List]: """Earnings Calendar. - Available providers: fmp, - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the company. - limit : int + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + limit : Optional[int] + The number of data entries to return. Returns ------- - symbol : str - The symbol of the asset. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + EarningsCalendar + ---------------- + symbol : ConstrainedStrValue + Symbol to get data for. date : date - The date of the earnings calendar. - eps : float + The date of the data. + eps : Optional[NonNegativeFloat] The EPS of the earnings calendar. - eps_estimated : float + eps_estimated : Optional[NonNegativeFloat] The estimated EPS of the earnings calendar. time : str The time of the earnings calendar. - revenue : int + revenue : Optional[int] The revenue of the earnings calendar. - revenue_estimated : int + revenue_estimated : Optional[int] The estimated revenue of the earnings calendar. - updated_from_date : date + updated_from_date : Optional[date] The updated from date of the earnings calendar. fiscal_date_ending : date The fiscal date ending of the earnings calendar. @@ -760,17 +1215,14 @@ class CLASS_stocks_fa(Container): fmp === - Source: https://site.financialmodelingprep.com/developer/docs/earnings-calendar-api/ - - Parameter - --------- + Parameters + ---------- All fields are standardized. - Returns - ------- - Documentation not available. - """ + EarningsCalendar + ---------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -801,21 +1253,36 @@ class CLASS_stocks_fa(Container): ) -> CommandOutput[typing.List]: """Number of Employees. - Available providers: fmp, - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the company. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. Returns ------- - symbol : str - The symbol of the company to retrieve the historical employees of. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + HistoricalEmployees + ------------------- + symbol : ConstrainedStrValue + Symbol to get data for. cik : int The CIK of the company to retrieve the historical employees of. acceptance_time : datetime @@ -824,7 +1291,7 @@ class CLASS_stocks_fa(Container): The date of reporting of the company employee. company_name : str The registered name of the company to retrieve the historical employees of. - form_type : float + form_type : str The form type of the company employee. filing_date : date The filing date of the company employee @@ -836,12 +1303,14 @@ class CLASS_stocks_fa(Container): fmp === - Source: https://site.financialmodelingprep.com/developer/docs/historical-numer-of-employees-api/ - - Parameter - --------- + Parameters + ---------- All fields are standardized. - """ + + + HistoricalEmployees + ------------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -899,7 +1368,7 @@ class CLASS_stocks_fa(Container): def est( self, symbol: str, - period: Literal["quarter", "annual"] = "annual", + period: Literal["quarterly", "annually"] = "annually", limit: int = 30, chart: bool = False, provider: Optional[Literal["fmp"]] = None, @@ -907,82 +1376,94 @@ class CLASS_stocks_fa(Container): ) -> CommandOutput[typing.List]: """Analyst Estimates. - Available providers: fmp, - Standard - ======== + openbb + ====== - Parameter - --------- - symbol : str - The symbol of the company. - period : Literal["quarter", "annual"] - The period of the analyst estimates. + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + period : Literal['quarterly', 'annually'] + Period of the data to return (quarterly or annually). limit : int - The limit of the analyst estimates. - + The number of data entries to return. Returns ------- - symbol : str - The symbol of the asset. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + AnalystEstimates + ---------------- + symbol : ConstrainedStrValue + Symbol to get data for. date : date - The date of the analyst estimates. + A specific date to get data for. estimated_revenue_low : int - The estimated revenue low of the analyst estimates. + The estimated revenue low. estimated_revenue_high : int - The estimated revenue high of the analyst estimates. + The estimated revenue high. estimated_revenue_avg : int - The estimated revenue average of the analyst estimates. + The estimated revenue average. estimated_ebitda_low : int - The estimated EBITDA low of the analyst estimates. + The estimated EBITDA low. estimated_ebitda_high : int - The estimated EBITDA high of the analyst estimates. + The estimated EBITDA high. estimated_ebitda_avg : int - The estimated EBITDA average of the analyst estimates. + The estimated EBITDA average. estimated_ebit_low : int - The estimated EBIT low of the analyst estimates. + The estimated EBIT low. estimated_ebit_high : int - The estimated EBIT high of the analyst estimates. + The estimated EBIT high. estimated_ebit_avg : int - The estimated EBIT average of the analyst estimates. + The estimated EBIT average. estimated_net_income_low : int - The estimated net income low of the analyst estimates. + The estimated net income low. estimated_net_income_high : int - The estimated net income high of the analyst estimates. + The estimated net income high. estimated_net_income_avg : int - The estimated net income average of the analyst estimates. + The estimated net income average. estimated_sga_expense_low : int - The estimated SGA expense low of the analyst estimates. + The estimated SGA expense low. estimated_sga_expense_high : int - The estimated SGA expense high of the analyst estimates. + The estimated SGA expense high. estimated_sga_expense_avg : int - The estimated SGA expense average of the analyst estimates. + The estimated SGA expense average. estimated_eps_avg : float - The estimated EPS average of the analyst estimates. + The estimated EPS average. estimated_eps_high : float - The estimated EPS high of the analyst estimates. + The estimated EPS high. estimated_eps_low : float - The estimated EPS low of the analyst estimates. + The estimated EPS low. number_analyst_estimated_revenue : int - The number of analysts who estimated revenue of the analyst estimates. + The number of analysts who estimated revenue. number_analysts_estimated_eps : int - The number of analysts who estimated EPS of the analyst estimates. + The number of analysts who estimated EPS. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/analyst-estimates-api/ + Parameters + ---------- + All fields are standardized. - Parameter - --------- - period: Literal["quarter", "annual"] - The period of the analyst estimates. - Returns - ------- - Documentation not available. - """ + AnalystEstimates + ---------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -1093,113 +1574,137 @@ class CLASS_stocks_fa(Container): self, symbol: str, period: Literal["annually", "quarterly"] = "annually", + limit: Optional[pydantic.types.NonNegativeInt] = 12, chart: bool = False, provider: Optional[Literal["fmp", "polygon"]] = None, **kwargs ) -> CommandOutput[typing.List]: """Income Statement. - Available providers: fmp, polygon - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the company. - period : Literal["annually", "quarterly"] - The period of the income statement. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp, polygon] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + period : Literal['annually', 'quarterly'] + Period of the data to return (quarterly or annually). + limit : Optional[NonNegativeInt] + The number of data entries to return. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + IncomeStatement + --------------- date : date - The date of the income statement. + Date of the income statement. symbol : str - The symbol of the company. - currency : Optional[str] - The currency of the income statement. + Symbol of the company. cik : Optional[int] - The Central Index Key (CIK) of the company. - filing_date : date - The filing date of the income statement. - accepted_date : Optional[date] - The accepted date of the income statement. + Central Index Key. + currency : Optional[str] + Reporting currency. + filing_date : Optional[date] + Filling date. + accepted_date : Optional[datetime] + Accepted date. period : Optional[str] - The period of the income statement. + Period of the income statement. revenue : Optional[int] - The revenue of the income statement. + Revenue. cost_of_revenue : Optional[int] - The cost of revenue of the income statement. + Cost of revenue. gross_profit : Optional[int] - The gross profit of the income statement. + Gross profit. research_and_development_expenses : Optional[int] - The research and development expenses of the income statement. + Research and development expenses. general_and_administrative_expenses : Optional[int] - The general and administrative expenses of the income statement. + General and administrative expenses. selling_and_marketing_expenses : Optional[float] - The selling and marketing expenses of the income statement. + Selling and marketing expenses. selling_general_and_administrative_expenses : Optional[int] - The selling, general and administrative expenses of the income statement. + Selling, general and administrative expenses. other_expenses : Optional[int] - The other expenses of the income statement. + Other expenses. operating_expenses : Optional[int] - The operating expenses of the income statement. + Operating expenses. depreciation_and_amortization : Optional[int] - The depreciation and amortization of the income statement. + Depreciation and amortization. ebitda : Optional[int] - The earnings before interest, taxes, depreciation and amortization of the income statement. + Earnings before interest, taxes, depreciation and amortization. operating_income : Optional[int] - The operating income of the income statement. + Operating income. interest_income : Optional[int] - The interest income of the income statement. + Interest income. interest_expense : Optional[int] - The interest expense of the income statement. + Interest expense. total_other_income_expenses_net : Optional[int] - The total other income expenses net of the income statement. + Total other income expenses net. income_before_tax : Optional[int] - The income before tax of the income statement. + Income before tax. income_tax_expense : Optional[int] - The income tax expense of the income statement. + Income tax expense. net_income : Optional[int] - The net income of the income statement. + Net income. eps : Optional[float] - The earnings per share of the income statement. + Earnings per share. eps_diluted : Optional[float] - The diluted earnings per share of the income statement. + Earnings per share diluted. weighted_average_shares_outstanding : Optional[int] - The weighted average shares outstanding of the income statement. + Weighted average shares outstanding. weighted_average_shares_outstanding_dil : Optional[int] - The weighted average shares outstanding diluted of the income statement. + Weighted average shares outstanding diluted. fmp === - Source: https://financialmodelingprep.com/developer/docs/#Income-Statement - - Parameter - --------- + Parameters + ---------- cik : Optional[str] The CIK of the company if no symbol is provided. - limit : Optional[NonNegativeInt] - The limit of the income statement. - Returns - ------- - Documentation not available. - + IncomeStatement + --------------- + calendarYear : Optional[int] + None + grossProfitRatio : Optional[float] + None + ebitdaratio : Optional[float] + None + operatingIncomeRatio : Optional[float] + None + incomeBeforeTaxRatio : Optional[float] + None + netIncomeRatio : Optional[float] + None + link : Optional[str] + None + finalLink : Optional[str] + None polygon ======= - Source: https://polygon.io/docs/stocks#!/get_vx_reference_financials - - Parameter - --------- - cik : Optional[str] - The CIK of the company if no symbol is provided. + Parameters + ---------- company_name : Optional[str] The name of the company. company_name_search : Optional[str] @@ -1226,22 +1731,30 @@ class CLASS_stocks_fa(Container): The period of report date greater than the given date. period_of_report_date_gte : Optional[date] The period of report date greater than or equal to the given date. - timeframe : Optional[Literal["annual", "quarterly", "ttm"]] - The timeframe of the financial statement. include_sources : Optional[bool] Whether to include the sources of the financial statement. - order : Optional[Literal["asc", "desc"]] + order : Optional[Literal['asc', 'desc']] The order of the financial statement. - limit : Optional[PositiveInt] - The limit of the financial statement. - sort : Optional[Literal["filing_date", "period_of_report_date"]] + sort : Optional[Literal['filing_date', 'period_of_report_date']] The sort of the financial statement. - Returns - ------- - Documentation not available. - """ + IncomeStatement + --------------- + income_loss_from_continuing_operations_after_tax : Optional[float] + None + benefits_costs_expenses : Optional[float] + None + net_income_loss_attributable_to_noncontrolling_interest : Optional[int] + None + net_income_loss_attributable_to_parent : Optional[float] + None + net_income_loss_available_to_common_stockholders_basic : Optional[float] + None + participating_securities_distributed_and_undistributed_earnings_loss_basic : Optional[float] + None + preferred_stock_dividends_and_other_adjustments : Optional[float] + None""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -1249,6 +1762,7 @@ class CLASS_stocks_fa(Container): standard_params={ "symbol": symbol, "period": period, + "limit": limit, }, extra_params=kwargs, chart=chart, @@ -1261,75 +1775,242 @@ class CLASS_stocks_fa(Container): return filter_output(o) + @filter_call + @validate_arguments + def income_growth( + self, + symbol: str, + limit: int = 10, + period: Literal["annually", "quarterly"] = "annually", + chart: bool = False, + provider: Optional[Literal["fmp"]] = None, + **kwargs + ) -> CommandOutput[typing.List]: + """Income Statement Growth. + + + openbb + ====== + + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + limit : int + The number of data entries to return. + period : Literal['annually', 'quarterly'] + Period of the data to return (quarterly or annually). + + Returns + ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + IncomeStatementGrowth + --------------------- + symbol : ConstrainedStrValue + Symbol to get data for. + date : date + The date of the data. + period : str + The period the statement is returned for. + growth_revenue : float + Growth rate of total revenue. + growth_cost_of_revenue : float + Growth rate of cost of goods sold. + growth_gross_profit : float + Growth rate of gross profit. + growth_gross_profit_ratio : float + Growth rate of gross profit as a percentage of revenue. + growth_research_and_development_expenses : float + Growth rate of expenses on research and development. + growth_general_and_administrative_expenses : float + Growth rate of general and administrative expenses. + growth_selling_and_marketing_expenses : float + Growth rate of expenses on selling and marketing activities. + growth_other_expenses : float + Growth rate of other operating expenses. + growth_operating_expenses : float + Growth rate of total operating expenses. + growth_cost_and_expenses : float + Growth rate of total costs and expenses. + growth_interest_expense : float + Growth rate of interest expenses. + growth_depreciation_and_amortization : float + Growth rate of depreciation and amortization expenses. + growth_ebitda : float + Growth rate of Earnings Before Interest, Taxes, Depreciation, and Amortization. + growth_ebitda_ratio : float + Growth rate of EBITDA as a percentage of revenue. + growth_operating_income : float + Growth rate of operating income. + growth_operating_income_ratio : float + Growth rate of operating income as a percentage of revenue. + growth_total_other_income_expenses_net : float + Growth rate of net total other income and expenses. + growth_income_before_tax : float + Growth rate of income before taxes. + growth_income_before_tax_ratio : float + Growth rate of income before taxes as a percentage of revenue. + growth_income_tax_expense : float + Growth rate of income tax expenses. + growth_net_income : float + Growth rate of net income. + growth_net_income_ratio : float + Growth rate of net income as a percentage of revenue. + growth_eps : float + Growth rate of Earnings Per Share (EPS). + growth_eps_diluted : float + Growth rate of diluted Earnings Per Share (EPS). + growth_weighted_average_shs_out : float + Growth rate of weighted average shares outstanding. + growth_weighted_average_shs_out_dil : float + Growth rate of diluted weighted average shares outstanding. + + fmp + === + + Parameters + ---------- + All fields are standardized. + + + IncomeStatementGrowth + --------------------- + All fields are standardized.""" + inputs = filter_inputs( + provider_choices={ + "provider": provider, + }, + standard_params={ + "symbol": symbol, + "limit": limit, + "period": period, + }, + extra_params=kwargs, + chart=chart, + ) + + o = self._command_runner_session.run( + "/stocks/fa/income_growth", + **inputs, + ).output + + return filter_output(o) + @filter_call @validate_arguments def ins( self, - transactionType: List[ - openbb_provider.models.stock_insider_trading.TransactionTypes - ] = [], - symbol: Optional[str] = None, + symbol: str, + transactionType: Optional[ + List[ + Literal[ + "A-Award", + "C-Conversion", + "D-Return", + "E-ExpireShort", + "F-InKind", + "G-Gift", + "H-ExpireLong", + "I-Discretionary", + "J-Other", + "L-Small", + "M-Exempt", + "O-OutOfTheMoney", + "P-Purchase", + "S-Sale", + "U-Tender", + "W-Will", + "X-InTheMoney", + "Z-Trust", + ] + ] + ] = ["P-Purchase"], reportingCik: Optional[int] = None, companyCik: Optional[int] = None, - page: int = 0, + page: Optional[int] = 0, chart: bool = False, provider: Optional[Literal["fmp"]] = None, **kwargs ) -> CommandOutput[typing.List]: """Stock Insider Trading. - Available providers: fmp, - Standard - ======== + openbb + ====== - - Parameter - --------- - transactionType : List[TransactionTypes] - The type of the transaction. Possible values are: - A-Award, C-Conversion, D-Return, E-ExpireShort, F-InKind, G-Gift, H-ExpireLong - I-Discretionary, J-Other, L-Small, M-Exempt, O-OutOfTheMoneym P-Purchase - S-Sale, U-Tender, W-Will, X-InTheMoney, Z-Trust - symbol : str] - The symbol of the company. - reportingCik : int] + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + transactionType : Optional[List[Literal['A-Award', 'C-Conversion', 'D-Return', 'E-ExpireShort', 'F-InKind', 'G-Gift', 'H-ExpireLong', 'I-Discretionary', 'J-Other', 'L-Small', 'M-Exempt', 'O-OutOfTheMoney', 'P-Purchase', 'S-Sale', 'U-Tender', 'W-Will', 'X-InTheMoney', 'Z-Trust']]] + The type of the transaction. + reportingCik : Optional[int] The CIK of the reporting owner. - companyCik : int] + companyCik : Optional[int] The CIK of the company owner. - page: int - The page number to get - + page : Optional[int] + The page number of the data to fetch. Returns ------- - symbol : str - The symbol of the asset. - filingDate : datetime + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + StockInsiderTrading + ------------------- + symbol : ConstrainedStrValue + Symbol to get data for. + filing_date : datetime The filing date of the stock insider trading. - transactionDate : date + transaction_date : date The transaction date of the stock insider trading. - reportingCik : int + reporting_cik : int The reporting CIK of the stock insider trading. - transactionType : str + transaction_type : str The transaction type of the stock insider trading. - securitiesOwned : int + securities_owned : int The securities owned of the stock insider trading. - companyCik : int + company_cik : int The company CIK of the stock insider trading. - reportingName : str + reporting_name : str The reporting name of the stock insider trading. - typeOfOwner : str + type_of_owner : str The type of owner of the stock insider trading. - acquistionOrDisposition : str + acquistion_or_disposition : str The acquistion or disposition of the stock insider trading. - formType : str + form_type : str The form type of the stock insider trading. - securitiesTransacted : float + securities_transacted : float The securities transacted of the stock insider trading. price : float The price of the stock insider trading. - securityName : str + security_name : str The security name of the stock insider trading. link : str The link of the stock insider trading. @@ -1337,26 +2018,21 @@ class CLASS_stocks_fa(Container): fmp === - Source: https://site.financialmodelingprep.com/developer/docs/#Stock-Insider-Trading + Parameters + ---------- + All fields are standardized. - Parameter - --------- - The type of the transaction. Possible values are: - A-Award, C-Conversion, D-Return, E-ExpireShort, F-InKind, G-Gift, H-ExpireLong - I-Discretionary, J-Other, L-Small, M-Exempt, O-OutOfTheMoneym P-Purchase - S-Sale, U-Tender, W-Will, X-InTheMoney, Z-Trust - companyCik: str] - The CIK of the company owner. - page: int - The page number to get - """ + + StockInsiderTrading + ------------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, }, standard_params={ - "transactionType": transactionType, "symbol": symbol, + "transactionType": transactionType, "reportingCik": reportingCik, "companyCik": companyCik, "page": page, @@ -1372,6 +2048,155 @@ class CLASS_stocks_fa(Container): return filter_output(o) + @filter_call + @validate_arguments + def ins_own( + self, + symbol: str, + include_current_quarter: bool = False, + date: Optional[datetime.date] = None, + chart: bool = False, + provider: Optional[Literal["fmp"]] = None, + **kwargs + ) -> CommandOutput[typing.List]: + """Institutional Ownership. + + + openbb + ====== + + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + include_current_quarter : bool + Include current quarter data. + date : Optional[date] + A specific date to get data for. + + Returns + ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + InstitutionalOwnership + ---------------------- + symbol : ConstrainedStrValue + Symbol to get data for. + cik : Optional[str] + The CIK of the company. + date : date + The date of the data. + investors_holding : int + The number of investors holding the stock. + last_investors_holding : int + The number of investors holding the stock in the last quarter. + investors_holding_change : int + The change in the number of investors holding the stock. + number_of_13f_shares : Optional[int] + The number of 13F shares. + last_number_of_13f_shares : Optional[int] + The number of 13F shares in the last quarter. + number_of_13f_shares_change : Optional[int] + The change in the number of 13F shares. + total_invested : float + The total amount invested. + last_total_invested : float + The total amount invested in the last quarter. + total_invested_change : float + The change in the total amount invested. + ownership_percent : float + The ownership percent. + last_ownership_percent : float + The ownership percent in the last quarter. + ownership_percent_change : float + The change in the ownership percent. + new_positions : int + The number of new positions. + last_new_positions : int + The number of new positions in the last quarter. + new_positions_change : int + he change in the number of new positions. + increased_positions : int + The number of increased positions. + last_increased_positions : int + The number of increased positions in the last quarter. + increased_positions_change : int + The change in the number of increased positions. + closed_positions : int + The number of closed positions. + last_closed_positions : int + The number of closed positions in the last quarter. + closed_positions_change : int + The change in the number of closed positions. + reduced_positions : int + The number of reduced positions. + last_reduced_positions : int + The number of reduced positions in the last quarter. + reduced_positions_change : int + The change in the number of reduced positions. + total_calls : int + Total number of call options contracts traded for Apple Inc. on the specified date. + last_total_calls : int + Total number of call options contracts traded for Apple Inc. on the previous reporting date. + total_calls_change : int + Change in the total number of call options contracts traded between the current and previous reporting dates. + total_puts : int + Total number of put options contracts traded for Apple Inc. on the specified date. + last_total_puts : int + Total number of put options contracts traded for Apple Inc. on the previous reporting date. + total_puts_change : int + Change in the total number of put options contracts traded between the current and previous reporting dates. + put_call_ratio : float + The put-call ratio, which is the ratio of the total number of put options to call options traded on the specified date. + last_put_call_ratio : float + The put-call ratio on the previous reporting date. + put_call_ratio_change : float + Change in the put-call ratio between the current and previous reporting dates. + + fmp + === + + Parameters + ---------- + All fields are standardized. + + + InstitutionalOwnership + ---------------------- + All fields are standardized.""" + inputs = filter_inputs( + provider_choices={ + "provider": provider, + }, + standard_params={ + "symbol": symbol, + "include_current_quarter": include_current_quarter, + "date": date, + }, + extra_params=kwargs, + chart=chart, + ) + + o = self._command_runner_session.run( + "/stocks/fa/ins_own", + **inputs, + ).output + + return filter_output(o) + @filter_call @validate_arguments def key( @@ -1394,161 +2219,177 @@ class CLASS_stocks_fa(Container): self, symbol: str, period: Literal["annually", "quarterly"] = "annually", - limit: Optional[int] = None, + limit: Optional[int] = 100, chart: bool = False, provider: Optional[Literal["fmp"]] = None, **kwargs ) -> CommandOutput[typing.List]: """Key Metrics. - Available providers: fmp, - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the company. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + period : Literal['annually', 'quarterly'] + Period of the data to return (quarterly or annually). + limit : Optional[int] + The number of data entries to return. Returns ------- - symbol : str - The symbol of the stock. - date : DateType - The date of the key metrics. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + KeyMetrics + ---------- + symbol : ConstrainedStrValue + Symbol to get data for. + date : date + The date of the data. period : str - The period of the key metrics. - revenue_per_share : float - The revenue per share of the stock. - net_income_per_share : float - The net income per share of the stock. - operating_cash_flow_per_share : float - The operating cash flow per share of the stock. - free_cash_flow_per_share : float - The free cash flow per share of the stock. - cash_per_share : float - The cash per share of the stock. - book_value_per_share : float - The book value per share of the stock. - tangible_book_value_per_share : float - The tangible book value per share of the stock. - shareholders_equity_per_share : float - The shareholders equity per share of the stock. - interest_debt_per_share : float - The interest debt per share of the stock. - market_cap : float - The market cap of the stock. - enterprise_value : float - The enterprise value of the stock. - pe_ratio : float - The PE ratio of the stock. - price_to_sales_ratio : float - The price to sales ratio of the stock. - pocf_ratio : float - The POCF ratio of the stock. - pfcf_ratio : float - The PFCF ratio of the stock. - pb_ratio : float - The PB ratio of the stock. - ptb_ratio : float - The PTB ratio of the stock. - ev_to_sales : float - The EV to sales of the stock. - enterprise_value_over_ebitda : float - The enterprise value over EBITDA of the stock. - ev_to_operating_cash_flow : float - The EV to operating cash flow of the stock. - ev_to_free_cash_flow : float - The EV to free cash flow of the stock. - earnings_yield : float - The earnings yield of the stock. - free_cash_flow_yield : float - The free cash flow yield of the stock. - debt_to_equity : float - The debt to equity of the stock. - debt_to_assets : float - The debt to assets of the stock. - net_debt_to_ebitda : float - The net debt to EBITDA of the stock. - current_ratio : float - The current ratio of the stock. - interest_coverage : float - The interest coverage of the stock. - income_quality : float - The income quality of the stock. - dividend_yield : float - The dividend yield of the stock. - payout_ratio : float - The payout ratio of the stock. - sales_general_and_administrative_to_revenue : float - The sales general and administrative to revenue of the stock. - research_and_developement_to_revenue : float - The research and development to revenue of the stock. - intangibles_to_total_assets : float - The intangibles to total assets of the stock. - capex_to_operating_cash_flow : float - The capex to operating cash flow of the stock. - capex_to_revenue : float - The capex to revenue of the stock. - capex_to_depreciation : float - The capex to depreciation of the stock. - stock_based_compensation_to_revenue : float - The stock based compensation to revenue of the stock. - graham_number : float - The graham number of the stock. - roic : float - The ROIC of the stock. - return_on_tangible_assets : float - The return on tangible assets of the stock. - graham_net_net : float - The graham net net of the stock. - working_capital : float - The working capital of the stock. - tangible_asset_value : float - The tangible asset value of the stock. - net_current_asset_value : float - The net current asset value of the stock. - invested_capital : float - The invested capital of the stock. - average_receivables : float - The average receivables of the stock. - average_payables : float - The average payables of the stock. - average_inventory : float - The average inventory of the stock. - days_sales_outstanding : float - The days sales outstanding of the stock. - days_payables_outstanding : float - The days payables outstanding of the stock. - days_of_inventory_on_hand : float - The days of inventory on hand of the stock. - receivables_turnover : float - The receivables turnover of the stock. - payables_turnover : float - The payables turnover of the stock. - inventory_turnover : float - The inventory turnover of the stock. - roe : float - The ROE of the stock. - capex_per_share : float - The capex per share of the stock. + The period of the data. + revenue_per_share : Optional[float] + Revenue per share + net_income_per_share : Optional[float] + Net income per share + operating_cash_flow_per_share : Optional[float] + Operating cash flow per share + free_cash_flow_per_share : Optional[float] + Free cash flow per share + cash_per_share : Optional[float] + Cash per share + book_value_per_share : Optional[float] + Book value per share + tangible_book_value_per_share : Optional[float] + Tangible book value per share + shareholders_equity_per_share : Optional[float] + Shareholders equity per share + interest_debt_per_share : Optional[float] + Interest debt per share + market_cap : Optional[float] + Market capitalization + enterprise_value : Optional[float] + Enterprise value + pe_ratio : Optional[float] + Price-to-earnings ratio (P/E ratio) + price_to_sales_ratio : Optional[float] + Price-to-sales ratio + pocf_ratio : Optional[float] + Price-to-operating cash flow ratio + pfcf_ratio : Optional[float] + Price-to-free cash flow ratio + pb_ratio : Optional[float] + Price-to-book ratio + ptb_ratio : Optional[float] + Price-to-tangible book ratio + ev_to_sales : Optional[float] + Enterprise value-to-sales ratio + enterprise_value_over_ebitda : Optional[float] + Enterprise value-to-EBITDA ratio + ev_to_operating_cash_flow : Optional[float] + Enterprise value-to-operating cash flow ratio + ev_to_free_cash_flow : Optional[float] + Enterprise value-to-free cash flow ratio + earnings_yield : Optional[float] + Earnings yield + free_cash_flow_yield : Optional[float] + Free cash flow yield + debt_to_equity : Optional[float] + Debt-to-equity ratio + debt_to_assets : Optional[float] + Debt-to-assets ratio + net_debt_to_ebitda : Optional[float] + Net debt-to-EBITDA ratio + current_ratio : Optional[float] + Current ratio + interest_coverage : Optional[float] + Interest coverage + income_quality : Optional[float] + Income quality + dividend_yield : Optional[float] + Dividend yield + payout_ratio : Optional[float] + Payout ratio + sales_general_and_administrative_to_revenue : Optional[float] + Sales general and administrative expenses-to-revenue ratio + research_and_development_to_revenue : Optional[float] + Research and development expenses-to-revenue ratio + intangibles_to_total_assets : Optional[float] + Intangibles-to-total assets ratio + capex_to_operating_cash_flow : Optional[float] + Capital expenditures-to-operating cash flow ratio + capex_to_revenue : Optional[float] + Capital expenditures-to-revenue ratio + capex_to_depreciation : Optional[float] + Capital expenditures-to-depreciation ratio + stock_based_compensation_to_revenue : Optional[float] + Stock-based compensation-to-revenue ratio + graham_number : Optional[float] + Graham number + roic : Optional[float] + Return on invested capital + return_on_tangible_assets : Optional[float] + Return on tangible assets + graham_net_net : Optional[float] + Graham net-net working capital + working_capital : Optional[float] + Working capital + tangible_asset_value : Optional[float] + Tangible asset value + net_current_asset_value : Optional[float] + Net current asset value + invested_capital : Optional[float] + Invested capital + average_receivables : Optional[float] + Average receivables + average_payables : Optional[float] + Average payables + average_inventory : Optional[float] + Average inventory + days_sales_outstanding : Optional[float] + Days sales outstanding + days_payables_outstanding : Optional[float] + Days payables outstanding + days_of_inventory_on_hand : Optional[float] + Days of inventory on hand + receivables_turnover : Optional[float] + Receivables turnover + payables_turnover : Optional[float] + Payables turnover + inventory_turnover : Optional[float] + Inventory turnover + roe : Optional[float] + Return on equity + capex_per_share : Optional[float] + Capital expenditures per share fmp === - Source: https://site.financialmodelingprep.com/developer/docs/company-key-metrics-api/ - - Parameter - --------- + Parameters + ---------- All fields are standardized. - Returns - ------- - Documentation not available. - """ + KeyMetrics + ---------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -1574,77 +2415,72 @@ class CLASS_stocks_fa(Container): def mgmt( self, symbol: str, - key_executive_name: Optional[str] = None, - key_executive_title: Optional[str] = None, - key_executive_title_since: Optional[datetime.datetime] = None, - key_executive_year_born: Optional[datetime.datetime] = None, - key_executive_gender: Optional[str] = None, chart: bool = False, provider: Optional[Literal["fmp"]] = None, **kwargs ) -> CommandOutput[typing.List]: """Key Executives. - Available providers: fmp, - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the company. - key_executive_name : Optional[str] - The name of the key executive to be retrieved. - key_executive_title : Optional[str] - The title of the key executive to be retrieved. - key_executive_title_since : Optional[datetime] - The period since the position of the key executive to be retrieved. - key_executive_year_born : Optional[datetime] - The year born of the key executive to be retrieved. - key_executive_gender: Optional[str] - The gender of the key executive to be retrieved. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. Returns ------- - name : Optional[str] - The name of the key executive. - title : Optional[str] - The title of the key executive. - title_since : Optional[datetime] - The title since of the key executive. - year_born : Optional[datetime] - The year born of the key executive. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + KeyExecutives + ------------- + title : str + Designation of the key executive. + name : str + Name of the key executive. + pay : Optional[int] + Pay of the key executive. + currency_pay : str + The currency of the pay. gender : Optional[str] - The gender of the key executive. + Gender of the key executive. + year_born : Optional[str] + Birth year of the key executive. + title_since : Optional[int] + Date the tile was held since. fmp === - Source: https://financialmodelingprep.com/developer/docs/#Key-Executives - - Parameter - --------- - key_executive_gender: Optional[str] - The gender of the key executive. + Parameters + ---------- + All fields are standardized. - Returns - ------- - Documentation not available. - """ + KeyExecutives + ------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, }, standard_params={ "symbol": symbol, - "key_executive_name": key_executive_name, - "key_executive_title": key_executive_title, - "key_executive_title_since": key_executive_title_since, - "key_executive_year_born": key_executive_year_born, - "key_executive_gender": key_executive_gender, }, extra_params=kwargs, chart=chart, @@ -1701,21 +2537,36 @@ class CLASS_stocks_fa(Container): ) -> CommandOutput[typing.List]: """Company Overview. - Available providers: fmp, - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the company. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. Returns ------- - symbol : str - The symbol of the company. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + CompanyOverview + --------------- + symbol : ConstrainedStrValue + Symbol to get data for. price : float The price of the company. beta : float @@ -1790,17 +2641,14 @@ class CLASS_stocks_fa(Container): fmp === - Source: https://site.financialmodelingprep.com/developer/docs/companies-key-stats-free-api/ - - Parameter - --------- + Parameters + ---------- All fields are standardized. - Returns - ------- - Documentation not available. - """ + CompanyOverview + --------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -1824,106 +2672,144 @@ class CLASS_stocks_fa(Container): def own( self, symbol: str, - include_current_quarter: bool = False, - date: Optional[datetime.date] = None, + date: datetime.date, + page: Optional[int] = 0, chart: bool = False, provider: Optional[Literal["fmp"]] = None, **kwargs ) -> CommandOutput[typing.List]: - """Institutional Ownership. + """Stock Ownership. - Available providers: fmp, - Standard - ======== + openbb + ====== - Parameter - --------- - symbol : str - The symbol of the company. - include_current_quarter : bool - Whether to include the current quarter. Default is False. - date : Optional[dateType] + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + date : date A specific date to get data for. - + page : Optional[int] + The page number of the data to fetch. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + StockOwnership + -------------- + date : date + The date of the data. + cik : int + The cik of the stock ownership. + filing_date : date + The filing date of the stock ownership. + investor_name : str + The investor name of the stock ownership. symbol : str - The symbol of the company. - cik : str - The CIK of the company. - date : dateType - The date of the institutional ownership. - investors_holding : int - The number of investors holding the stock. - last_investors_holding : int - The number of investors holding the stock in the last quarter. - investors_holding_change : int - The change in the number of investors holding the stock. - number_of_13f_shares : int - The number of 13F shares. - last_number_of_13f_shares : int - The number of 13F shares in the last quarter. - number_of_13f_shares_change : int - The change in the number of 13F shares. - total_invested : float - The total amount invested. - last_total_invested : float - The total amount invested in the last quarter. - total_invested_change : float - The change in the total amount invested. - ownership_percent : float - The ownership percent. - last_ownership_percent : float - The ownership percent in the last quarter. - ownership_percent_change : float - The change in the ownership percent. - new_positions : int - The number of new positions. - last_new_positions : int - The number of new positions in the last quarter. - new_positions_change : int - The change in the number of new positions. - increased_positions : int - The number of increased positions. - last_increased_positions : int - The number of increased positions in the last quarter. - increased_positions_change : int - The change in the number of increased positions. - closed_positions : int - The number of closed positions. - last_closed_positions : int - The number of closed positions in the last quarter. - closed_positions_change : int - The change in the number of closed positions. - reduced_positions : int - The number of reduced positions. - last_reduced_positions : int - The number of reduced positions in the last quarter. + The symbol of the stock ownership. + security_name : str + The security name of the stock ownership. + type_of_security : str + The type of security of the stock ownership. + security_cusip : str + The security cusip of the stock ownership. + shares_type : str + The shares type of the stock ownership. + put_call_share : str + The put call share of the stock ownership. + investment_discretion : str + The investment discretion of the stock ownership. + industry_title : str + The industry title of the stock ownership. + weight : float + The weight of the stock ownership. + last_weight : float + The last weight of the stock ownership. + change_in_weight : float + The change in weight of the stock ownership. + change_in_weight_percentage : float + The change in weight percentage of the stock ownership. + market_value : int + The market value of the stock ownership. + last_market_value : int + The last market value of the stock ownership. + change_in_market_value : int + The change in market value of the stock ownership. + change_in_market_value_percentage : float + The change in market value percentage of the stock ownership. + shares_number : int + The shares number of the stock ownership. + last_shares_number : int + The last shares number of the stock ownership. + change_in_shares_number : float + The change in shares number of the stock ownership. + change_in_shares_number_percentage : float + The change in shares number percentage of the stock ownership. + quarter_end_price : float + The quarter end price of the stock ownership. + avg_price_paid : float + The average price paid of the stock ownership. + is_new : bool + Is the stock ownership new. + is_sold_out : bool + Is the stock ownership sold out. + ownership : float + How much is the ownership. + last_ownership : float + The last ownership amount. + change_in_ownership : float + The change in ownership amount. + change_in_ownership_percentage : float + The change in ownership percentage. + holding_period : int + The holding period of the stock ownership. + first_added : date + The first added date of the stock ownership. + performance : float + The performance of the stock ownership. + performance_percentage : float + The performance percentage of the stock ownership. + last_performance : float + The last performance of the stock ownership. + change_in_performance : float + The change in performance of the stock ownership. + is_counted_for_performance : bool + Is the stock ownership counted for performance. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/institutional-stock-ownership-api/ - - Parameter - --------- + Parameters + ---------- All fields are standardized. - Returns - ------- - Documentation not available. - """ + StockOwnership + -------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, }, standard_params={ "symbol": symbol, - "include_current_quarter": include_current_quarter, "date": date, + "page": page, }, extra_params=kwargs, chart=chart, @@ -1947,21 +2833,36 @@ class CLASS_stocks_fa(Container): ) -> CommandOutput[typing.List]: """Price Target Consensus. - Available providers: fmp, - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the company. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. Returns ------- - symbol : str - The symbol of the asset. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + PriceTargetConsensus + -------------------- + symbol : ConstrainedStrValue + Symbol to get data for. target_high : float The high target of the price target consensus. target_low : float @@ -1974,17 +2875,14 @@ class CLASS_stocks_fa(Container): fmp === - Source: https://site.financialmodelingprep.com/developer/docs/price-target-consensus-api/ - - Parameter - --------- + Parameters + ---------- All fields are standardized. - Returns - ------- - Documentation not available. - """ + PriceTargetConsensus + -------------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -2014,28 +2912,43 @@ class CLASS_stocks_fa(Container): ) -> CommandOutput[typing.List]: """Price Target. - Available providers: fmp, - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the company. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. Returns ------- - symbol : str - The symbol of the asset. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + PriceTarget + ----------- + symbol : ConstrainedStrValue + Symbol to get data for. published_date : datetime The published date of the price target. news_url : str The news URL of the price target. - news_title : str + news_title : Optional[str] The news title of the price target. - analyst_name : str + analyst_name : Optional[str] The analyst name of the price target. price_target : float The price target of the price target. @@ -2053,17 +2966,14 @@ class CLASS_stocks_fa(Container): fmp === - Source: https://site.financialmodelingprep.com/developer/docs/#Price-Target - - Parameter - --------- + Parameters + ---------- All fields are standardized. - Returns - ------- - Documentation not available. - """ + PriceTarget + ----------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -2138,7 +3048,7 @@ class CLASS_stocks_fa(Container): def revgeo( self, symbol: str, - period: Literal["quarterly", "annually"] = "quarterly", + period: Literal["quarterly", "annually"] = "annually", structure: Literal["hierarchical", "flat"] = "flat", chart: bool = False, provider: Optional[Literal["fmp"]] = None, @@ -2146,50 +3056,62 @@ class CLASS_stocks_fa(Container): ) -> CommandOutput[typing.List]: """Revenue Geographic. - Available providers: fmp, - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the company. - period : Literal["quarterly", "annually"] - The period of the income statement. - structure : Literal["hierarchical", "flat"] - The structure of the revenue geographic. Should always be flat. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + period : Literal['quarterly', 'annually'] + Period of the data to return (quarterly or annually). + structure : Literal['hierarchical', 'flat'] + The structure of the returned data. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + RevenueGeographic + ----------------- date : date - The date of the revenue. + The date of the data. americas : Optional[int] - The revenue of the America segment. + The revenue from the the American segment. europe : Optional[int] - The revenue of the Europe segment. + The revenue from the the European segment. greater_china : Optional[int] - The revenue of the Greater China segment. + The revenue from the the Greater China segment. japan : Optional[int] - The revenue of the Japan segment. + The revenue from the the Japan segment. rest_of_asia_pacific : Optional[int] - The revenue of the Rest of Asia Pacific segment. + The revenue from the the Rest of Asia Pacific segment. fmp === - Source: https://financialmodelingprep.com/developer/docs/#Revenue-Geographic-Segmentation - - Parameter - --------- + Parameters + ---------- All fields are standardized. - Returns - ------- - Documentation not available. - """ + RevenueGeographic + ----------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -2215,7 +3137,7 @@ class CLASS_stocks_fa(Container): def revseg( self, symbol: str, - period: Literal["quarterly", "annually"] = "quarterly", + period: Literal["quarterly", "annually"] = "annually", structure: Literal["hierarchical", "flat"] = "flat", chart: bool = False, provider: Optional[Literal["fmp"]] = None, @@ -2223,43 +3145,54 @@ class CLASS_stocks_fa(Container): ) -> CommandOutput[typing.List]: """Revenue Business Line. - Available providers: fmp, - Standard - ======== - - - Parameter - --------- - symbol : str - The symbol of the company. - period : Literal["quarterly", "annually"] - The period of the income statement. - structure : Literal["hierarchical", "flat"] - The structure of the revenue business line. Should always be flat. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. + period : Literal['quarterly', 'annually'] + Period of the data to return (quarterly or annually). + structure : Literal['hierarchical', 'flat'] + The structure of the returned data. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + RevenueBusinessLine + ------------------- date : date - The date of the revenue. - data_and_service : Dict[str, int] - The data and service of the revenue. + The date of the data. + business_line : Mapping[str, int] + Day level data containing the revenue of the business line. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/sales-revenue-by-segments-api/ - - Parameter - --------- + Parameters + ---------- All fields are standardized. - Returns - ------- - Documentation not available. - """ + RevenueBusinessLine + ------------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -2357,46 +3290,58 @@ class CLASS_stocks_fa(Container): ) -> CommandOutput[typing.List]: """Share Statistics. - Available providers: fmp, - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the company. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. Returns ------- - symbol : str - The symbol of the company. - date : dateType - The date of the share statistics. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + ShareStatistics + --------------- + symbol : ConstrainedStrValue + Symbol to get data for. + date : date + A specific date to get data for. free_float : float - The free float of the company. + The percentage of unrestricted shares of a publicly-traded company. float_shares : float - The float shares of the company. + The number of shares available for trading by the general public. outstanding_shares : float - The outstanding shares of the company. + The total number of shares of a publicly-traded company. source : str - The source of the data. + Source of the received data. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/shares-float-api/ - - Parameter - --------- + Parameters + ---------- All fields are standardized. - Returns - ------- - Documentation not available. - """ + ShareStatistics + --------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -2426,21 +3371,36 @@ class CLASS_stocks_fa(Container): ) -> CommandOutput[typing.List]: """Historical Stock Splits. - Available providers: fmp, - Standard - ======== - - Parameter - --------- - symbol : str - The symbol of the company. + openbb + ====== + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. Returns ------- + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + HistoricalStockSplits + --------------------- date : date - The date of the historical stock splits. + The date of the data. label : str The label of the historical stock splits. numerator : float @@ -2451,12 +3411,14 @@ class CLASS_stocks_fa(Container): fmp === - Source: https://site.financialmodelingprep.com/developer/docs/historical-stock-splits-api/ - - Parameter - --------- + Parameters + ---------- All fields are standardized. - """ + + + HistoricalStockSplits + --------------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -2498,48 +3460,67 @@ class CLASS_stocks_fa(Container): self, symbol: str, year: int, + quarter: Literal[1, 2, 3, 4] = 1, chart: bool = False, provider: Optional[Literal["fmp"]] = None, **kwargs ) -> CommandOutput[typing.List]: """Earnings Call Transcript. - Available providers: fmp, - Standard - ======== + openbb + ====== - Parameter - --------- - symbol : str - The symbol of the company. + Parameters + ---------- + provider: Literal[fmp] + The provider to use for the query. + symbol : ConstrainedStrValue + Symbol to get data for. year : int The year of the earnings call transcript. - + quarter : Literal[1, 2, 3, 4] + The quarter of the earnings call transcript. Returns ------- - symbol : str - The symbol of the asset. + CommandOutput + results: List[Data] + Serializable results. + provider: Optional[PROVIDERS] + Provider name. + warnings: Optional[List[Warning_]] + List of warnings. + error: Optional[Error] + Caught exceptions. + chart: Optional[Chart] + Chart object. + + + EarningsCallTranscript + ---------------------- + symbol : ConstrainedStrValue + Symbol to get data for. quarter : int The quarter of the earnings call transcript. year : int The year of the earnings call transcript. date : datetime - The date of the earnings call transcript. + The date of the data. content : str The content of the earnings call transcript. fmp === - Source: https://site.financialmodelingprep.com/developer/docs/earnings-calendar-api/ + Parameters + ---------- + All fields are standardized. - Parameter - --------- - year: int - The year of the transcript to get. - """ + + EarningsCallTranscript + ---------------------- + All fields are standardized.""" inputs = filter_inputs( provider_choices={ "provider": provider, @@ -2547,6 +3528,7 @@ class CLASS_stocks_fa(Container): standard_params={ "symbol": symbol, "year": year, + "quarter": quarter, }, extra_params=kwargs, chart=chart, diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/package/ta.py b/openbb_sdk/sdk/core/openbb_core/app/static/package/ta.py index 1e82589462e..0536c0944b3 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/package/ta.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/package/ta.py @@ -1,63 +1,56 @@ ### THIS FILE IS AUTO-GENERATED. DO NOT EDIT. ### -import typing -from typing import List, Literal, Optional, Union - +from openbb_core.app.static.container import Container +from openbb_core.app.model.command_output import CommandOutput import openbb_provider import pandas +import datetime import pydantic -import pydantic.types from pydantic import validate_arguments - -import openbb_core.app.model.results.empty -from openbb_core.app.model.command_output import CommandOutput -from openbb_core.app.static.container import Container +from inspect import Parameter +from typing import List, Dict, Union, Optional, Literal +from openbb_core.app.utils import df_to_basemodel from openbb_core.app.static.filters import filter_call, filter_inputs, filter_output +import openbb_core.app.model.results.empty +import pydantic.types +import typing class CLASS_ta(Container): + @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def atr( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - length: pydantic.types.PositiveInt = 14, - mamode: Literal["rma", "ema", "sma", "wma"] = "rma", - drift: pydantic.types.NonNegativeInt = 1, - offset: int = 0, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def atr(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', length: pydantic.types.PositiveInt = 14, mamode: Literal['rma', 'ema', 'sma', 'wma'] = 'rma', drift: pydantic.types.NonNegativeInt = 1, offset: int = 0, chart: bool = False) -> CommandOutput[typing.List]: """ - Average True Range is used to measure volatility, especially volatility caused by - gaps or limit moves. + Average True Range is used to measure volatility, especially volatility caused by + gaps or limit moves. - Parameters - ---------- - data : List[Data] - List of data to apply the indicator to. - index : str, optional - Index column name, by default "date" - length : PositiveInt, optional - It's period, by default 14 - mamode : Literal["rma", "ema", "sma", "wma"], optional - Moving average mode, by default "rma" - drift : NonNegativeInt, optional - The difference period, by default 1 - offset : int, optional - How many periods to offset the result, by default 0 + Parameters + ---------- + data : List[Data] + List of data to apply the indicator to. + index : str, optional + Index column name, by default "date" + length : PositiveInt, optional + It's period, by default 14 + mamode : Literal["rma", "ema", "sma", "wma"], optional + Moving average mode, by default "rma" + drift : NonNegativeInt, optional + The difference period, by default 1 + offset : int, optional + How many periods to offset the result, by default 0 - Returns - ------- - CommandOutput[List[Data]] - List of data with the indicator applied. + Returns + ------- + CommandOutput[List[Data]] + List of data with the indicator applied. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> atr_data = obb.ta.atr(data=stock_data.results) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> atr_data = obb.ta.atr(data=stock_data.results) + """ inputs = filter_inputs( data=data, index=index, @@ -77,39 +70,30 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def fib( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - close_column: Literal["close", "adj_close"] = "close", - period: pydantic.types.PositiveInt = 120, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def fib(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', close_column: Literal['close', 'adj_close'] = 'close', period: pydantic.types.PositiveInt = 120, start_date: Optional[str] = None, end_date: Optional[str] = None, chart: bool = False) -> CommandOutput[typing.List]: """ - Fibonacci Retracement Levels. + Fibonacci Retracement Levels. - Parameters - ---------- - data : List[Data] - List of data to apply the indicator to. - index : str, optional - Index column name, by default "date" - period : PositiveInt, optional - Period to calculate the indicator, by default 120 + Parameters + ---------- + data : List[Data] + List of data to apply the indicator to. + index : str, optional + Index column name, by default "date" + period : PositiveInt, optional + Period to calculate the indicator, by default 120 - Returns - ------- - CommandOutput[List[Data]] - List of data with the indicator applied. + Returns + ------- + CommandOutput[List[Data]] + List of data with the indicator applied. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> fib_data = obb.ta.fib(data=stock_data.results, period=120) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> fib_data = obb.ta.fib(data=stock_data.results, period=120) + """ inputs = filter_inputs( data=data, index=index, @@ -129,44 +113,38 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def obv( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - offset: int = 0, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def obv(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', offset: int = 0, chart: bool = False) -> CommandOutput[typing.List]: """ - The On Balance Volume (OBV) is a cumulative total of the up and - down volume. When the close is higher than the previous close, the volume is added - to the running total, and when the close is lower than the previous close, the volume - is subtracted from the running total. + The On Balance Volume (OBV) is a cumulative total of the up and + down volume. When the close is higher than the previous close, the volume is added + to the running total, and when the close is lower than the previous close, the volume + is subtracted from the running total. + + To interpret the OBV, look for the OBV + to move with the price or precede price moves. If the price moves before the OBV, + then it is a non-confirmed move. A series of rising peaks, or falling troughs, in the + OBV indicates a strong trend. If the OBV is flat, then the market is not trending. - To interpret the OBV, look for the OBV - to move with the price or precede price moves. If the price moves before the OBV, - then it is a non-confirmed move. A series of rising peaks, or falling troughs, in the - OBV indicates a strong trend. If the OBV is flat, then the market is not trending. + Parameters + ---------- + data : List[Data] + List of data to apply the indicator to. + index : str, optional + Index column name, by default "date" + offset : int, optional + How many periods to offset the result, by default 0. - Parameters - ---------- - data : List[Data] - List of data to apply the indicator to. - index : str, optional - Index column name, by default "date" - offset : int, optional - How many periods to offset the result, by default 0. + Returns + ------- + CommandOutput[List[Data]] + List of data with the indicator applied. - Returns - ------- - CommandOutput[List[Data]] - List of data with the indicator applied. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> obv_data = obb.ta.obv(data=stock_data.results, offset=0) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> obv_data = obb.ta.obv(data=stock_data.results, offset=0) + """ inputs = filter_inputs( data=data, index=index, @@ -183,43 +161,36 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def fisher( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - length: pydantic.types.PositiveInt = 14, - signal: pydantic.types.PositiveInt = 1, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def fisher(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', length: pydantic.types.PositiveInt = 14, signal: pydantic.types.PositiveInt = 1, chart: bool = False) -> CommandOutput[typing.List]: """ - The Fisher Transform is a technical indicator created by John F. Ehlers - that converts prices into a Gaussian normal distribution.1 The indicator - highlights when prices have moved to an extreme, based on recent prices. - This may help in spotting turning points in the price of an asset. It also - helps show the trend and isolate the price waves within a trend. + The Fisher Transform is a technical indicator created by John F. Ehlers + that converts prices into a Gaussian normal distribution.1 The indicator + highlights when prices have moved to an extreme, based on recent prices. + This may help in spotting turning points in the price of an asset. It also + helps show the trend and isolate the price waves within a trend. - Parameters - ---------- - data : List[Data] - List of data to apply the indicator to. - index : str, optional - Index column name, by default "date" - length : PositiveInt, optional - Fisher period, by default 14 - signal : PositiveInt, optional - Fisher Signal period, by default 1 + Parameters + ---------- + data : List[Data] + List of data to apply the indicator to. + index : str, optional + Index column name, by default "date" + length : PositiveInt, optional + Fisher period, by default 14 + signal : PositiveInt, optional + Fisher Signal period, by default 1 - Returns - ------- - CommandOutput[List[Data]] - List of data with the indicator applied. + Returns + ------- + CommandOutput[List[Data]] + List of data with the indicator applied. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> fisher_data = obb.ta.fisher(data=stock_data.results, length=14, signal=1) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> fisher_data = obb.ta.fisher(data=stock_data.results, length=14, signal=1) + """ inputs = filter_inputs( data=data, index=index, @@ -237,45 +208,37 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def adosc( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - fast: pydantic.types.PositiveInt = 3, - slow: pydantic.types.PositiveInt = 10, - offset: int = 0, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def adosc(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', fast: pydantic.types.PositiveInt = 3, slow: pydantic.types.PositiveInt = 10, offset: int = 0, chart: bool = False) -> CommandOutput[typing.List]: """ - Accumulation/Distribution Oscillator, also known as the Chaikin Oscillator - is essentially a momentum indicator, but of the Accumulation-Distribution line - rather than merely price. It looks at both the strength of price moves and the - underlying buying and selling pressure during a given time period. The oscillator - reading above zero indicates net buying pressure, while one below zero registers - net selling pressure. Divergence between the indicator and pure price moves are - the most common signals from the indicator, and often flag market turning points. + Accumulation/Distribution Oscillator, also known as the Chaikin Oscillator + is essentially a momentum indicator, but of the Accumulation-Distribution line + rather than merely price. It looks at both the strength of price moves and the + underlying buying and selling pressure during a given time period. The oscillator + reading above zero indicates net buying pressure, while one below zero registers + net selling pressure. Divergence between the indicator and pure price moves are + the most common signals from the indicator, and often flag market turning points. - Parameters - ---------- - data : List[Data] - List of data to be used for the calculation. - fast : PositiveInt, optional - Number of periods to be used for the fast calculation, by default 3. - slow : PositiveInt, optional - Number of periods to be used for the slow calculation, by default 10. - offset : int, optional - Offset to be used for the calculation, by default 0. + Parameters + ---------- + data : List[Data] + List of data to be used for the calculation. + fast : PositiveInt, optional + Number of periods to be used for the fast calculation, by default 3. + slow : PositiveInt, optional + Number of periods to be used for the slow calculation, by default 10. + offset : int, optional + Offset to be used for the calculation, by default 0. - Returns - ------- - CommandOutput[List[Data]] + Returns + ------- + CommandOutput[List[Data]] - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> adosc_data = obb.ta.adosc(data=stock_data.results, fast=3, slow=10, offset=0) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> adosc_data = obb.ta.adosc(data=stock_data.results, fast=3, slow=10, offset=0) + """ inputs = filter_inputs( data=data, index=index, @@ -294,9 +257,7 @@ class CLASS_ta(Container): @filter_call @validate_arguments - def tv( - self, chart: bool = False - ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: + def tv(self, chart: bool = False) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """TradingView.""" inputs = filter_inputs( chart=chart, @@ -311,61 +272,51 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def bbands( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - target: str = "close", - index: str = "date", - length: int = 50, - std: pydantic.types.NonNegativeFloat = 2, - mamode: Literal["sma", "ema", "wma", "rma"] = "sma", - offset: int = 0, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def bbands(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], target: str = 'close', index: str = 'date', length: int = 50, std: pydantic.types.NonNegativeFloat = 2, mamode: Literal['sma', 'ema', 'wma', 'rma'] = 'sma', offset: int = 0, chart: bool = False) -> CommandOutput[typing.List]: """ - Bollinger Bands consist of three lines. The middle band is a simple - moving average (generally 20 periods) of the typical price (TP). The upper and lower - bands are F standard deviations (generally 2) above and below the middle band. - The bands widen and narrow when the volatility of the price is higher or lower, - respectively. + Bollinger Bands consist of three lines. The middle band is a simple + moving average (generally 20 periods) of the typical price (TP). The upper and lower + bands are F standard deviations (generally 2) above and below the middle band. + The bands widen and narrow when the volatility of the price is higher or lower, + respectively. + +Bollinger Bands do not, in themselves, generate buy or sell signals; + they are an indicator of overbought or oversold conditions. When the price is near the + upper or lower band it indicates that a reversal may be imminent. The middle band + becomes a support or resistance level. The upper and lower bands can also be + interpreted as price targets. When the price bounces off of the lower band and crosses + the middle band, then the upper band becomes the price target. - Bollinger Bands do not, in themselves, generate buy or sell signals; - they are an indicator of overbought or oversold conditions. When the price is near the - upper or lower band it indicates that a reversal may be imminent. The middle band - becomes a support or resistance level. The upper and lower bands can also be - interpreted as price targets. When the price bounces off of the lower band and crosses - the middle band, then the upper band becomes the price target. + Parameters + ---------- + data : List[Data] + List of data to be used for the calculation. + target : str + Target column name. + index : str, optional + Index column name to use with `data`, by default "date". + length : int, optional + Number of periods to be used for the calculation, by default 50. + std : NonNegativeFloat, optional + Standard deviation to be used for the calculation, by default 2. + mamode : Literal["sma", "ema", "wma", "rma"], optional + Moving average mode to be used for the calculation, by default "sma". + offset : int, optional + Offset to be used for the calculation, by default 0. - Parameters - ---------- - data : List[Data] - List of data to be used for the calculation. - target : str - Target column name. - index : str, optional - Index column name to use with `data`, by default "date". - length : int, optional - Number of periods to be used for the calculation, by default 50. - std : NonNegativeFloat, optional - Standard deviation to be used for the calculation, by default 2. - mamode : Literal["sma", "ema", "wma", "rma"], optional - Moving average mode to be used for the calculation, by default "sma". - offset : int, optional - Offset to be used for the calculation, by default 0. + Returns + ------- + CommandOutput[List[Data]] + The calculated data. - Returns - ------- - CommandOutput[List[Data]] - The calculated data. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> bbands = obb.ta.bbands( - >>> data=stock_data.results, target="close", length=50, std=2, mamode="sma", offset=0 - >>> ) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> bbands = obb.ta.bbands( + >>> data=stock_data.results, target="close", length=50, std=2, mamode="sma", offset=0 + >>> ) + """ inputs = filter_inputs( data=data, target=target, @@ -386,9 +337,7 @@ class CLASS_ta(Container): @filter_call @validate_arguments - def multi( - self, chart: bool = False - ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: + def multi(self, chart: bool = False) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """Plot multiple indicators on the same chart.""" inputs = filter_inputs( chart=chart, @@ -403,48 +352,40 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def zlma( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - target: str = "close", - index: str = "date", - length: int = 50, - offset: int = 0, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def zlma(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], target: str = 'close', index: str = 'date', length: int = 50, offset: int = 0, chart: bool = False) -> CommandOutput[typing.List]: """ - The zero lag exponential moving average (ZLEMA) indicator - was created by John Ehlers and Ric Way. The idea is do a - regular exponential moving average (EMA) calculation but - on a de-lagged data instead of doing it on the regular data. - Data is de-lagged by removing the data from "lag" days ago - thus removing (or attempting to) the cumulative effect of - the moving average. + The zero lag exponential moving average (ZLEMA) indicator + was created by John Ehlers and Ric Way. The idea is do a + regular exponential moving average (EMA) calculation but + on a de-lagged data instead of doing it on the regular data. + Data is de-lagged by removing the data from "lag" days ago + thus removing (or attempting to) the cumulative effect of + the moving average. - Parameters - ---------- - data : List[Data] - List of data to be used for the calculation. - target : str - Target column name. - index : str, optional - Index column name to use with `data`, by default "date". - length : int, optional - Number of periods to be used for the calculation, by default 50. - offset : int, optional - Offset to be used for the calculation, by default 0. + Parameters + ---------- + data : List[Data] + List of data to be used for the calculation. + target : str + Target column name. + index : str, optional + Index column name to use with `data`, by default "date". + length : int, optional + Number of periods to be used for the calculation, by default 50. + offset : int, optional + Offset to be used for the calculation, by default 0. - Returns - ------- - CommandOutput[List[Data]] - The calculated data. + Returns + ------- + CommandOutput[List[Data]] + The calculated data. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> zlma_data = obb.ta.zlma(data=stock_data.results, target="close", length=50, offset=0) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> zlma_data = obb.ta.zlma(data=stock_data.results, target="close", length=50, offset=0) + """ inputs = filter_inputs( data=data, target=target, @@ -463,50 +404,43 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def aroon( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - length: int = 25, - scalar: int = 100, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def aroon(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', length: int = 25, scalar: int = 100, chart: bool = False) -> CommandOutput[typing.List]: """ - The word aroon is Sanskrit for "dawn's early light." The Aroon - indicator attempts to show when a new trend is dawning. The indicator consists - of two lines (Up and Down) that measure how long it has been since the highest - high/lowest low has occurred within an n period range. + The word aroon is Sanskrit for "dawn's early light." The Aroon + indicator attempts to show when a new trend is dawning. The indicator consists + of two lines (Up and Down) that measure how long it has been since the highest + high/lowest low has occurred within an n period range. + + When the Aroon Up is + staying between 70 and 100 then it indicates an upward trend. When the Aroon Down + is staying between 70 and 100 then it indicates an downward trend. A strong upward + trend is indicated when the Aroon Up is above 70 while the Aroon Down is below 30. + Likewise, a strong downward trend is indicated when the Aroon Down is above 70 while + the Aroon Up is below 30. Also look for crossovers. When the Aroon Down crosses above + the Aroon Up, it indicates a weakening of the upward trend (and vice versa). - When the Aroon Up is - staying between 70 and 100 then it indicates an upward trend. When the Aroon Down - is staying between 70 and 100 then it indicates an downward trend. A strong upward - trend is indicated when the Aroon Up is above 70 while the Aroon Down is below 30. - Likewise, a strong downward trend is indicated when the Aroon Down is above 70 while - the Aroon Up is below 30. Also look for crossovers. When the Aroon Down crosses above - the Aroon Up, it indicates a weakening of the upward trend (and vice versa). + Parameters + ---------- + data : List[Data] + List of data to be used for the calculation. + index: str, optional + Index column name to use with `data`, by default "date". + length : int, optional + Number of periods to be used for the calculation, by default 25. + scalar : int, optional + Scalar to be used for the calculation, by default 100. - Parameters - ---------- - data : List[Data] - List of data to be used for the calculation. - index: str, optional - Index column name to use with `data`, by default "date". - length : int, optional - Number of periods to be used for the calculation, by default 25. - scalar : int, optional - Scalar to be used for the calculation, by default 100. + Returns + ------- + CommandOutput[List[Data]] + The calculated data. - Returns - ------- - CommandOutput[List[Data]] - The calculated data. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> aroon_data = obb.ta.aroon(data=stock_data.results, length=25, scalar=100) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> aroon_data = obb.ta.aroon(data=stock_data.results, length=25, scalar=100) + """ inputs = filter_inputs( data=data, index=index, @@ -524,48 +458,40 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def sma( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - target: str = "close", - index: str = "date", - length: int = 50, - offset: int = 0, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def sma(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], target: str = 'close', index: str = 'date', length: int = 50, offset: int = 0, chart: bool = False) -> CommandOutput[typing.List]: """ - Moving Averages are used to smooth the data in an array to - help eliminate noise and identify trends. The Simple Moving Average is literally - the simplest form of a moving average. Each output value is the average of the - previous n values. In a Simple Moving Average, each value in the time period carries - equal weight, and values outside of the time period are not included in the average. - This makes it less responsive to recent changes in the data, which can be useful for - filtering out those changes. + Moving Averages are used to smooth the data in an array to + help eliminate noise and identify trends. The Simple Moving Average is literally + the simplest form of a moving average. Each output value is the average of the + previous n values. In a Simple Moving Average, each value in the time period carries + equal weight, and values outside of the time period are not included in the average. + This makes it less responsive to recent changes in the data, which can be useful for + filtering out those changes. - Parameters - ---------- - data : List[Data] - List of data to be used for the calculation. - target : str - Target column name. - index : str, optional - Index column name to use with `data`, by default "date". - length : int, optional - Number of periods to be used for the calculation, by default 50. - offset : int, optional - Offset from the current period, by default 0. + Parameters + ---------- + data : List[Data] + List of data to be used for the calculation. + target : str + Target column name. + index : str, optional + Index column name to use with `data`, by default "date". + length : int, optional + Number of periods to be used for the calculation, by default 50. + offset : int, optional + Offset from the current period, by default 0. - Returns - ------- - CommandOutput[List[Data]] - The calculated data. + Returns + ------- + CommandOutput[List[Data]] + The calculated data. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> sma_data = obb.ta.sma(data=stock_data.results,target="close",length=50,offset=0) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> sma_data = obb.ta.sma(data=stock_data.results,target="close",length=50,offset=0) + """ inputs = filter_inputs( data=data, target=target, @@ -584,45 +510,36 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def demark( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - target: str = "close", - show_all: bool = False, - asint: bool = False, - offset: int = 0, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def demark(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', target: str = 'close', show_all: bool = False, asint: bool = False, offset: int = 0, chart: bool = False) -> CommandOutput[typing.List]: """ - Demark sequential indicator + Demark sequential indicator - Parameters - ---------- - data : List[Data] - List of data to be used for the calculation. - index : str, optional - Index column name to use with `data`, by default "date". - target : str, optional - Target column name, by default "close". - show_all : bool, optional - Show 1 - 13. If set to False, show 6 - 9 - asint : bool, optional - If True, fill NAs with 0 and change type to int, by default False - offset : int, optional - How many periods to offset the result + Parameters + ---------- + data : List[Data] + List of data to be used for the calculation. + index : str, optional + Index column name to use with `data`, by default "date". + target : str, optional + Target column name, by default "close". + show_all : bool, optional + Show 1 - 13. If set to False, show 6 - 9 + asint : bool, optional + If True, fill NAs with 0 and change type to int, by default False + offset : int, optional + How many periods to offset the result - Returns - ------- - CommandOutput[List[Data]] - The calculated data. + Returns + ------- + CommandOutput[List[Data]] + The calculated data. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> demark_data = obb.ta.demark(data=stock_data.results,offset=0) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> demark_data = obb.ta.demark(data=stock_data.results,offset=0) + """ inputs = filter_inputs( data=data, index=index, @@ -642,42 +559,35 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def vwap( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - anchor: str = "D", - offset: int = 0, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def vwap(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', anchor: str = 'D', offset: int = 0, chart: bool = False) -> CommandOutput[typing.List]: """ - The Volume Weighted Average Price that measures the average typical price - by volume. It is typically used with intraday charts to identify general direction. + The Volume Weighted Average Price that measures the average typical price + by volume. It is typically used with intraday charts to identify general direction. - Parameters - ---------- - data : List[Data] - List of data to be used for the calculation. - index : str, optional - Index column name to use with `data`, by default "date". - anchor : str, optional - Anchor period to use for the calculation, by default "D". - See Timeseries Offset Aliases below for additional options: - https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#timeseries-offset-aliases - offset : int, optional - Offset from the current period, by default 0. + Parameters + ---------- + data : List[Data] + List of data to be used for the calculation. + index : str, optional + Index column name to use with `data`, by default "date". + anchor : str, optional + Anchor period to use for the calculation, by default "D". + See Timeseries Offset Aliases below for additional options: + https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#timeseries-offset-aliases + offset : int, optional + Offset from the current period, by default 0. - Returns - ------- - CommandOutput[List[Data]] - The calculated data. + Returns + ------- + CommandOutput[List[Data]] + The calculated data. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> vwap_data = obb.ta.vwap(data=stock_data.results,anchor="D",offset=0) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> vwap_data = obb.ta.vwap(data=stock_data.results,anchor="D",offset=0) + """ inputs = filter_inputs( data=data, index=index, @@ -695,9 +605,7 @@ class CLASS_ta(Container): @filter_call @validate_arguments - def recom( - self, chart: bool = False - ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: + def recom(self, chart: bool = False) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """Recommendation.""" inputs = filter_inputs( chart=chart, @@ -712,53 +620,44 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def macd( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - target: str = "close", - index: str = "date", - fast: int = 12, - slow: int = 26, - signal: int = 9, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def macd(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], target: str = 'close', index: str = 'date', fast: int = 12, slow: int = 26, signal: int = 9, chart: bool = False) -> CommandOutput[typing.List]: """ - The Moving Average Convergence Divergence (MACD) is the difference - between two Exponential Moving Averages. The Signal line is an Exponential Moving - Average of the MACD. + The Moving Average Convergence Divergence (MACD) is the difference + between two Exponential Moving Averages. The Signal line is an Exponential Moving + Average of the MACD. + + The MACD signals trend changes and indicates the start + of new trend direction. High values indicate overbought conditions, low values + indicate oversold conditions. Divergence with the price indicates an end to the + current trend, especially if the MACD is at extreme high or low values. When the MACD + line crosses above the signal line a buy signal is generated. When the MACD crosses + below the signal line a sell signal is generated. To confirm the signal, the MACD + should be above zero for a buy, and below zero for a sell. - The MACD signals trend changes and indicates the start - of new trend direction. High values indicate overbought conditions, low values - indicate oversold conditions. Divergence with the price indicates an end to the - current trend, especially if the MACD is at extreme high or low values. When the MACD - line crosses above the signal line a buy signal is generated. When the MACD crosses - below the signal line a sell signal is generated. To confirm the signal, the MACD - should be above zero for a buy, and below zero for a sell. + Parameters + ---------- + data : List[Data] + List of data to be used for the calculation. + target : str + Target column name. + fast : int, optional + Number of periods for the fast EMA, by default 12. + slow : int, optional + Number of periods for the slow EMA, by default 26. + signal : int, optional + Number of periods for the signal EMA, by default 9. - Parameters - ---------- - data : List[Data] - List of data to be used for the calculation. - target : str - Target column name. - fast : int, optional - Number of periods for the fast EMA, by default 12. - slow : int, optional - Number of periods for the slow EMA, by default 26. - signal : int, optional - Number of periods for the signal EMA, by default 9. + Returns + ------- + CommandOutput[List[Data]] + The calculated data. - Returns - ------- - CommandOutput[List[Data]] - The calculated data. - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> macd_data = obb.ta.macd(data=stock_data.results,target="close",fast=12,slow=26,signal=9) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> macd_data = obb.ta.macd(data=stock_data.results,target="close",fast=12,slow=26,signal=9) + """ inputs = filter_inputs( data=data, target=target, @@ -778,45 +677,37 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def hma( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - target: str = "close", - index: str = "date", - length: int = 50, - offset: int = 0, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def hma(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], target: str = 'close', index: str = 'date', length: int = 50, offset: int = 0, chart: bool = False) -> CommandOutput[typing.List]: """ - The Hull Moving Average solves the age old dilemma of making a moving average - more responsive to current price activity whilst maintaining curve smoothness. - In fact the HMA almost eliminates lag altogether and manages to improve smoothing - at the same time. + The Hull Moving Average solves the age old dilemma of making a moving average + more responsive to current price activity whilst maintaining curve smoothness. + In fact the HMA almost eliminates lag altogether and manages to improve smoothing + at the same time. - Parameters - ---------- - data : List[Data] - List of data to be used for the calculation. - target : str - Target column name. - index : str, optional - Index column name to use with `data`, by default "date". - length : int, optional - Number of periods for the HMA, by default 50. - offset : int, optional - Offset of the HMA, by default 0. + Parameters + ---------- + data : List[Data] + List of data to be used for the calculation. + target : str + Target column name. + index : str, optional + Index column name to use with `data`, by default "date". + length : int, optional + Number of periods for the HMA, by default 50. + offset : int, optional + Offset of the HMA, by default 0. - Returns - ------- - CommandOutput[List[Data]] - The calculated data. + Returns + ------- + CommandOutput[List[Data]] + The calculated data. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> hma_data = obb.ta.hma(data=stock_data.results,target="close",length=50,offset=0) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> hma_data = obb.ta.hma(data=stock_data.results,target="close",length=50,offset=0) + """ inputs = filter_inputs( data=data, target=target, @@ -835,47 +726,39 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def donchian( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - lower_length: pydantic.types.PositiveInt = 20, - upper_length: pydantic.types.PositiveInt = 20, - offset: int = 0, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def donchian(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', lower_length: pydantic.types.PositiveInt = 20, upper_length: pydantic.types.PositiveInt = 20, offset: int = 0, chart: bool = False) -> CommandOutput[typing.List]: """ - Donchian Channels are three lines generated by moving average - calculations that comprise an indicator formed by upper and lower - bands around a midrange or median band. The upper band marks the - highest price of a security over N periods while the lower band - marks the lowest price of a security over N periods. The area - between the upper and lower bands represents the Donchian Channel. + Donchian Channels are three lines generated by moving average + calculations that comprise an indicator formed by upper and lower + bands around a midrange or median band. The upper band marks the + highest price of a security over N periods while the lower band + marks the lowest price of a security over N periods. The area + between the upper and lower bands represents the Donchian Channel. - Parameters - ---------- - data : List[Data] - List of data to be used for the calculation. - index : str, optional - Index column name to use with `data`, by default "date". - lower_length : PositiveInt, optional - Number of periods for the lower band, by default 20. - upper_length : PositiveInt, optional - Number of periods for the upper band, by default 20. - offset : int, optional - Offset of the Donchian Channel, by default 0. + Parameters + ---------- + data : List[Data] + List of data to be used for the calculation. + index : str, optional + Index column name to use with `data`, by default "date". + lower_length : PositiveInt, optional + Number of periods for the lower band, by default 20. + upper_length : PositiveInt, optional + Number of periods for the upper band, by default 20. + offset : int, optional + Offset of the Donchian Channel, by default 0. - Returns - ------- - CommandOutput[List[Data]] - The calculated data. + Returns + ------- + CommandOutput[List[Data]] + The calculated data. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> donchian_data = obb.ta.donchian(data=stock_data.results,lower_length=20,upper_length=20,offset=0) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> donchian_data = obb.ta.donchian(data=stock_data.results,lower_length=20,upper_length=20,offset=0) + """ inputs = filter_inputs( data=data, index=index, @@ -894,40 +777,30 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def ichimoku( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - conversion: pydantic.types.PositiveInt = 9, - base: pydantic.types.PositiveInt = 26, - lagging: pydantic.types.PositiveInt = 52, - offset: pydantic.types.PositiveInt = 26, - lookahead: bool = False, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def ichimoku(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', conversion: pydantic.types.PositiveInt = 9, base: pydantic.types.PositiveInt = 26, lagging: pydantic.types.PositiveInt = 52, offset: pydantic.types.PositiveInt = 26, lookahead: bool = False, chart: bool = False) -> CommandOutput[typing.List]: """ - The Ichimoku Cloud, also known as Ichimoku Kinko Hyo, is a versatile indicator that - defines support and resistance, identifies trend direction, gauges momentum and provides - trading signals. Ichimoku Kinko Hyo translates into "one look equilibrium chart". With - one look, chartists can identify the trend and look for potential signals within that trend. + The Ichimoku Cloud, also known as Ichimoku Kinko Hyo, is a versatile indicator that + defines support and resistance, identifies trend direction, gauges momentum and provides + trading signals. Ichimoku Kinko Hyo translates into "one look equilibrium chart". With + one look, chartists can identify the trend and look for potential signals within that trend. - Parameters - ---------- - data : List[Data] - List of data to be used for the calculation. - index : str, optional - Index column name to use with `data`, by default "date". - conversion : PositiveInt, optional - Number of periods for the conversion line, by default 9. - base : PositiveInt, optional - Number of periods for the base line, by default 26. - lagging : PositiveInt, optional - Number of periods for the lagging span, by default 52. - offset : PositiveInt, optional - Number of periods for the offset, by default 26. - lookahead : bool, optional - drops the Chikou Span Column to prevent potential data leak - """ + Parameters + ---------- + data : List[Data] + List of data to be used for the calculation. + index : str, optional + Index column name to use with `data`, by default "date". + conversion : PositiveInt, optional + Number of periods for the conversion line, by default 9. + base : PositiveInt, optional + Number of periods for the base line, by default 26. + lagging : PositiveInt, optional + Number of periods for the lagging span, by default 52. + offset : PositiveInt, optional + Number of periods for the offset, by default 26. + lookahead : bool, optional + drops the Chikou Span Column to prevent potential data leak + """ inputs = filter_inputs( data=data, index=index, @@ -948,39 +821,32 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def clenow( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - target: str = "adj_close", - period: pydantic.types.PositiveInt = 90, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def clenow(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', target: str = 'adj_close', period: pydantic.types.PositiveInt = 90, chart: bool = False) -> CommandOutput[typing.List]: """ - Clenow Volatility Adjusted Momentum. + Clenow Volatility Adjusted Momentum. - Parameters - ---------- - data : List[Data] - List of data to be used for the calculation. - index : str, optional - Index column name to use with `data`, by default "date". - target : str, optional - Target column name, by default "adj_close". - period : PositiveInt, optional - Number of periods for the momentum, by default 90. + Parameters + ---------- + data : List[Data] + List of data to be used for the calculation. + index : str, optional + Index column name to use with `data`, by default "date". + target : str, optional + Target column name, by default "adj_close". + period : PositiveInt, optional + Number of periods for the momentum, by default 90. - Returns - ------- - CommandOutput[List[Data]] - The calculated data. + Returns + ------- + CommandOutput[List[Data]] + The calculated data. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> clenow_data = obb.ta.clenow(data=stock_data.results,period=90) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> clenow_data = obb.ta.clenow(data=stock_data.results,period=90) + """ inputs = filter_inputs( data=data, index=index, @@ -998,46 +864,40 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def ad( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - offset: int = 0, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def ad(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', offset: int = 0, chart: bool = False) -> CommandOutput[typing.List]: """ - The Accumulation/Distribution Line is similar to the On Balance - Volume (OBV), which sums the volume times +1/-1 based on whether the close is - higher than the previous close. The Accumulation/Distribution indicator, however - multiplies the volume by the close location value (CLV). The CLV is based on the - movement of the issue within a single bar and can be +1, -1 or zero. + The Accumulation/Distribution Line is similar to the On Balance + Volume (OBV), which sums the volume times +1/-1 based on whether the close is + higher than the previous close. The Accumulation/Distribution indicator, however + multiplies the volume by the close location value (CLV). The CLV is based on the + movement of the issue within a single bar and can be +1, -1 or zero. + + The Accumulation/Distribution Line is interpreted by looking for a divergence in + the direction of the indicator relative to price. If the Accumulation/Distribution + Line is trending upward it indicates that the price may follow. Also, if the + Accumulation/Distribution Line becomes flat while the price is still rising (or falling) + then it signals an impending flattening of the price. - The Accumulation/Distribution Line is interpreted by looking for a divergence in - the direction of the indicator relative to price. If the Accumulation/Distribution - Line is trending upward it indicates that the price may follow. Also, if the - Accumulation/Distribution Line becomes flat while the price is still rising (or falling) - then it signals an impending flattening of the price. + Parameters + ---------- + data : List[Data] + List of data to be used for the calculation. + index : str, optional + Index column name to use with `data`, by default "date". + offset : int, optional + Offset of the AD, by default 0. - Parameters - ---------- - data : List[Data] - List of data to be used for the calculation. - index : str, optional - Index column name to use with `data`, by default "date". - offset : int, optional - Offset of the AD, by default 0. + Returns + ------- + CommandOutput[List[Data]] - Returns - ------- - CommandOutput[List[Data]] - - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> ad_data = obb.ta.ad(data=stock_data.results,offset=0) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> ad_data = obb.ta.ad(data=stock_data.results,offset=0) + """ inputs = filter_inputs( data=data, index=index, @@ -1054,44 +914,36 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def adx( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - length: int = 50, - scalar: float = 100.0, - drift: int = 1, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def adx(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', length: int = 50, scalar: float = 100.0, drift: int = 1, chart: bool = False) -> CommandOutput[typing.List]: """ - The ADX is a Welles Wilder style moving average of the Directional Movement Index (DX). - The values range from 0 to 100, but rarely get above 60. To interpret the ADX, consider - a high number to be a strong trend, and a low number, a weak trend. + The ADX is a Welles Wilder style moving average of the Directional Movement Index (DX). + The values range from 0 to 100, but rarely get above 60. To interpret the ADX, consider + a high number to be a strong trend, and a low number, a weak trend. - Parameters - ---------- - data : List[Data] - List of data to be used for the calculation. - index : str, optional - Index column name to use with `data`, by default "date". - length : int, optional - Number of periods for the ADX, by default 50. - scalar : float, optional - Scalar value for the ADX, by default 100.0. - drift : int, optional - Drift value for the ADX, by default 1. + Parameters + ---------- + data : List[Data] + List of data to be used for the calculation. + index : str, optional + Index column name to use with `data`, by default "date". + length : int, optional + Number of periods for the ADX, by default 50. + scalar : float, optional + Scalar value for the ADX, by default 100.0. + drift : int, optional + Drift value for the ADX, by default 1. - Returns - ------- - CommandOutput[List[Data]] - The calculated data. + Returns + ------- + CommandOutput[List[Data]] + The calculated data. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> adx_data = obb.ta.adx(data=stock_data.results,length=50,scalar=100.0,drift=1) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> adx_data = obb.ta.adx(data=stock_data.results,length=50,scalar=100.0,drift=1) + """ inputs = filter_inputs( data=data, index=index, @@ -1110,45 +962,37 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def wma( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - target: str = "close", - index: str = "date", - length: int = 50, - offset: int = 0, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def wma(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], target: str = 'close', index: str = 'date', length: int = 50, offset: int = 0, chart: bool = False) -> CommandOutput[typing.List]: """ - A Weighted Moving Average puts more weight on recent data and less on past data. - This is done by multiplying each bar’s price by a weighting factor. Because of its - unique calculation, WMA will follow prices more closely than a corresponding Simple - Moving Average. + A Weighted Moving Average puts more weight on recent data and less on past data. + This is done by multiplying each bar’s price by a weighting factor. Because of its + unique calculation, WMA will follow prices more closely than a corresponding Simple + Moving Average. - Parameters - ---------- - data : List[Data] - The data to use for the calculation. - target : str - Target column name. - index : str, optional - Index column name to use with `data`, by default "date". - length : int, optional - The length of the WMA, by default 50. - offset : int, optional - The offset of the WMA, by default 0. + Parameters + ---------- + data : List[Data] + The data to use for the calculation. + target : str + Target column name. + index : str, optional + Index column name to use with `data`, by default "date". + length : int, optional + The length of the WMA, by default 50. + offset : int, optional + The offset of the WMA, by default 0. - Returns - ------- - CommandOutput[List[Data]] - The WMA data. + Returns + ------- + CommandOutput[List[Data]] + The WMA data. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> wma_data = obb.ta.wma(data=stock_data.results, target="close", length=50, offset=0) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> wma_data = obb.ta.wma(data=stock_data.results, target="close", length=50, offset=0) + """ inputs = filter_inputs( data=data, target=target, @@ -1167,37 +1011,30 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def cci( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - length: pydantic.types.PositiveInt = 14, - scalar: pydantic.types.PositiveFloat = 0.015, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def cci(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', length: pydantic.types.PositiveInt = 14, scalar: pydantic.types.PositiveFloat = 0.015, chart: bool = False) -> CommandOutput[typing.List]: """ - The CCI is designed to detect beginning and ending market trends. - The range of 100 to -100 is the normal trading range. CCI values outside of this - range indicate overbought or oversold conditions. You can also look for price - divergence in the CCI. If the price is making new highs, and the CCI is not, - then a price correction is likely. + The CCI is designed to detect beginning and ending market trends. + The range of 100 to -100 is the normal trading range. CCI values outside of this + range indicate overbought or oversold conditions. You can also look for price + divergence in the CCI. If the price is making new highs, and the CCI is not, + then a price correction is likely. - Parameters - ---------- - data : List[Data] - The data to use for the CCI calculation. - index : str, optional - Index column name to use with `data`, by default "date". - length : PositiveInt, optional - The length of the CCI, by default 14. - scalar : PositiveFloat, optional - The scalar of the CCI, by default 0.015. + Parameters + ---------- + data : List[Data] + The data to use for the CCI calculation. + index : str, optional + Index column name to use with `data`, by default "date". + length : PositiveInt, optional + The length of the CCI, by default 14. + scalar : PositiveFloat, optional + The scalar of the CCI, by default 0.015. - Returns - ------- - CommandOutput[List[Data]] - The CCI data. - """ + Returns + ------- + CommandOutput[List[Data]] + The CCI data. + """ inputs = filter_inputs( data=data, index=index, @@ -1215,49 +1052,40 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def rsi( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - target: str = "close", - index: str = "date", - length: int = 14, - scalar: float = 100.0, - drift: int = 1, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def rsi(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], target: str = 'close', index: str = 'date', length: int = 14, scalar: float = 100.0, drift: int = 1, chart: bool = False) -> CommandOutput[typing.List]: """ - The Relative Strength Index (RSI) calculates a ratio of the - recent upward price movements to the absolute price movement. The RSI ranges - from 0 to 100. The RSI is interpreted as an overbought/oversold indicator when - the value is over 70/below 30. You can also look for divergence with price. If - the price is making new highs/lows, and the RSI is not, it indicates a reversal. + The Relative Strength Index (RSI) calculates a ratio of the + recent upward price movements to the absolute price movement. The RSI ranges + from 0 to 100. The RSI is interpreted as an overbought/oversold indicator when + the value is over 70/below 30. You can also look for divergence with price. If + the price is making new highs/lows, and the RSI is not, it indicates a reversal. - Parameters - ---------- - data : List[Data] - The data to use for the RSI calculation. - target : str - Target column name. - index : str, optional - Index column name to use with `data`, by default "date" - length : int, optional - The length of the RSI, by default 14 - scalar : float, optional - The scalar to use for the RSI, by default 100.0 - drift : int, optional - The drift to use for the RSI, by default 1 + Parameters + ---------- + data : List[Data] + The data to use for the RSI calculation. + target : str + Target column name. + index : str, optional + Index column name to use with `data`, by default "date" + length : int, optional + The length of the RSI, by default 14 + scalar : float, optional + The scalar to use for the RSI, by default 100.0 + drift : int, optional + The drift to use for the RSI, by default 1 - Returns - ------- - CommandOutput[List[Data]] - The RSI data. + Returns + ------- + CommandOutput[List[Data]] + The RSI data. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> rsi_data = obb.ta.rsi(data=stock_data.results, target="close", length=14, scalar=100.0, drift=1) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> rsi_data = obb.ta.rsi(data=stock_data.results, target="close", length=14, scalar=100.0, drift=1) + """ inputs = filter_inputs( data=data, target=target, @@ -1277,9 +1105,7 @@ class CLASS_ta(Container): @filter_call @validate_arguments - def summary( - self, chart: bool = False - ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: + def summary(self, chart: bool = False) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """Summary.""" inputs = filter_inputs( chart=chart, @@ -1294,47 +1120,39 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def stoch( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - fast_k_period: pydantic.types.NonNegativeInt = 14, - slow_d_period: pydantic.types.NonNegativeInt = 3, - slow_k_period: pydantic.types.NonNegativeInt = 3, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def stoch(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', fast_k_period: pydantic.types.NonNegativeInt = 14, slow_d_period: pydantic.types.NonNegativeInt = 3, slow_k_period: pydantic.types.NonNegativeInt = 3, chart: bool = False) -> CommandOutput[typing.List]: """ - The Stochastic Oscillator measures where the close is in relation - to the recent trading range. The values range from zero to 100. %D values over 75 - indicate an overbought condition; values under 25 indicate an oversold condition. - When the Fast %D crosses above the Slow %D, it is a buy signal; when it crosses - below, it is a sell signal. The Raw %K is generally considered too erratic to use - for crossover signals. + The Stochastic Oscillator measures where the close is in relation + to the recent trading range. The values range from zero to 100. %D values over 75 + indicate an overbought condition; values under 25 indicate an oversold condition. + When the Fast %D crosses above the Slow %D, it is a buy signal; when it crosses + below, it is a sell signal. The Raw %K is generally considered too erratic to use + for crossover signals. - Parameters - ---------- - data : List[Data] - The data to use for the Stochastic Oscillator calculation. - index : str, optional - Index column name to use with `data`, by default "date". - fast_k_period : NonNegativeInt, optional - The fast %K period, by default 14. - slow_d_period : NonNegativeInt, optional - The slow %D period, by default 3. - slow_k_period : NonNegativeInt, optional - The slow %K period, by default 3. + Parameters + ---------- + data : List[Data] + The data to use for the Stochastic Oscillator calculation. + index : str, optional + Index column name to use with `data`, by default "date". + fast_k_period : NonNegativeInt, optional + The fast %K period, by default 14. + slow_d_period : NonNegativeInt, optional + The slow %D period, by default 3. + slow_k_period : NonNegativeInt, optional + The slow %K period, by default 3. - Returns - ------- - CommandOutput[List[Data]] - The Stochastic Oscillator data. + Returns + ------- + CommandOutput[List[Data]] + The Stochastic Oscillator data. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> stoch_data = obb.ta.stoch(data=stock_data.results, fast_k_period=14, slow_d_period=3, slow_k_period=3) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> stoch_data = obb.ta.stoch(data=stock_data.results, fast_k_period=14, slow_d_period=3, slow_k_period=3) + """ inputs = filter_inputs( data=data, index=index, @@ -1353,9 +1171,7 @@ class CLASS_ta(Container): @filter_call @validate_arguments - def rsp( - self, chart: bool = False - ) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: + def rsp(self, chart: bool = False) -> CommandOutput[openbb_core.app.model.results.empty.Empty]: """Relative Strength Performance.""" inputs = filter_inputs( chart=chart, @@ -1370,49 +1186,40 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def kc( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - length: pydantic.types.PositiveInt = 20, - scalar: pydantic.types.PositiveFloat = 20, - mamode: Literal["ema", "sma", "wma", "hma", "zlma"] = "ema", - offset: pydantic.types.NonNegativeInt = 0, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def kc(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', length: pydantic.types.PositiveInt = 20, scalar: pydantic.types.PositiveFloat = 20, mamode: Literal['ema', 'sma', 'wma', 'hma', 'zlma'] = 'ema', offset: pydantic.types.NonNegativeInt = 0, chart: bool = False) -> CommandOutput[typing.List]: """ - Keltner Channels are volatility-based bands that are placed - on either side of an asset's price and can aid in determining - the direction of a trend.The Keltner channel uses the average - true range (ATR) or volatility, with breaks above or below the top - and bottom barriers signaling a continuation. + Keltner Channels are volatility-based bands that are placed + on either side of an asset's price and can aid in determining + the direction of a trend.The Keltner channel uses the average + true range (ATR) or volatility, with breaks above or below the top + and bottom barriers signaling a continuation. - Parameters - ---------- - data : List[Data] - The data to use for the Keltner Channels calculation. - index : str, optional - Index column name to use with `data`, by default "date" - length : PositiveInt, optional - The length of the Keltner Channels, by default 20 - scalar : PositiveFloat, optional - The scalar to use for the Keltner Channels, by default 20 - mamode : Literal["ema", "sma", "wma", "hma", "zlma"], optional - The moving average mode to use for the Keltner Channels, by default "ema" - offset : NonNegativeInt, optional - The offset to use for the Keltner Channels, by default 0 + Parameters + ---------- + data : List[Data] + The data to use for the Keltner Channels calculation. + index : str, optional + Index column name to use with `data`, by default "date" + length : PositiveInt, optional + The length of the Keltner Channels, by default 20 + scalar : PositiveFloat, optional + The scalar to use for the Keltner Channels, by default 20 + mamode : Literal["ema", "sma", "wma", "hma", "zlma"], optional + The moving average mode to use for the Keltner Channels, by default "ema" + offset : NonNegativeInt, optional + The offset to use for the Keltner Channels, by default 0 - Returns - ------- - CommandOutput[List[Data]] - The Keltner Channels data. + Returns + ------- + CommandOutput[List[Data]] + The Keltner Channels data. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> kc_data = obb.ta.kc(data=stock_data.results, length=20, scalar=20, ma_mode="ema", offset=0) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> kc_data = obb.ta.kc(data=stock_data.results, length=20, scalar=20, ma_mode="ema", offset=0) + """ inputs = filter_inputs( data=data, index=index, @@ -1432,40 +1239,34 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def cg( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - length: pydantic.types.PositiveInt = 14, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def cg(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', length: pydantic.types.PositiveInt = 14, chart: bool = False) -> CommandOutput[typing.List]: """ - The Center of Gravity indicator, in short, is used to anticipate future price movements - and to trade on price reversals as soon as they happen. However, just like other oscillators, - the COG indicator returns the best results in range-bound markets and should be avoided when - the price is trending. Traders who use it will be able to closely speculate the upcoming - price change of the asset. + The Center of Gravity indicator, in short, is used to anticipate future price movements + and to trade on price reversals as soon as they happen. However, just like other oscillators, + the COG indicator returns the best results in range-bound markets and should be avoided when + the price is trending. Traders who use it will be able to closely speculate the upcoming + price change of the asset. - Parameters - ---------- - data : List[Data] - The data to use for the COG calculation. - index : str, optional - Index column name to use with `data`, by default "date" - length : PositiveInt, optional - The length of the COG, by default 14 + Parameters + ---------- + data : List[Data] + The data to use for the COG calculation. + index : str, optional + Index column name to use with `data`, by default "date" + length : PositiveInt, optional + The length of the COG, by default 14 - Returns - ------- - CommandOutput[List[Data]] - The COG data. + Returns + ------- + CommandOutput[List[Data]] + The COG data. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> cg_data = obb.ta.cg(data=stock_data.results, length=14) - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> cg_data = obb.ta.cg(data=stock_data.results, length=14) + """ inputs = filter_inputs( data=data, index=index, @@ -1482,73 +1283,57 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def cones( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - index: str = "date", - lower_q: float = 0.25, - upper_q: float = 0.75, - model: Literal[ - "STD", - "Parkinson", - "Garman-Klass", - "Hodges-Tompkins", - "Rogers-Satchell", - "Yang-Zhang", - ] = "STD", - is_crypto: bool = False, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def cones(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], index: str = 'date', lower_q: float = 0.25, upper_q: float = 0.75, model: Literal['STD', 'Parkinson', 'Garman-Klass', 'Hodges-Tompkins', 'Rogers-Satchell', 'Yang-Zhang'] = 'STD', is_crypto: bool = False, chart: bool = False) -> CommandOutput[typing.List]: """Calculate the realized volatility quantiles over rolling windows of time. - The model for calculating volatility is selectable. + The model for calculating volatility is selectable. - Parameters - ---------- - data : List[Data] - The data to use for the calculation. - index : str, optional - Index column name to use with `data`, by default "date" - lower_q : float, optional - The lower quantile value for calculations - upper_q : float, optional - The upper quantile value for calculations - model : Literal["STD", "Parkinson", "Garman-Klass", "Hodges-Tompkins", "Rogers-Satchell", "Yang-Zhang"], optional - The model used to calculate realized volatility + Parameters + ---------- + data : List[Data] + The data to use for the calculation. + index : str, optional + Index column name to use with `data`, by default "date" + lower_q : float, optional + The lower quantile value for calculations + upper_q : float, optional + The upper quantile value for calculations + model : Literal["STD", "Parkinson", "Garman-Klass", "Hodges-Tompkins", "Rogers-Satchell", "Yang-Zhang"], optional + The model used to calculate realized volatility - Standard deviation measures how widely returns are dispersed from the average return. - It is the most common (and biased) estimator of volatility. + Standard deviation measures how widely returns are dispersed from the average return. + It is the most common (and biased) estimator of volatility. - Parkinson volatility uses the high and low price of the day rather than just close to close prices. - It is useful for capturing large price movements during the day. + Parkinson volatility uses the high and low price of the day rather than just close to close prices. + It is useful for capturing large price movements during the day. - Garman-Klass volatility extends Parkinson volatility by taking into account the opening and closing price. - As markets are most active during the opening and closing of a trading session; - it makes volatility estimation more accurate. + Garman-Klass volatility extends Parkinson volatility by taking into account the opening and closing price. + As markets are most active during the opening and closing of a trading session; + it makes volatility estimation more accurate. - Hodges-Tompkins volatility is a bias correction for estimation using an overlapping data sample. - It produces unbiased estimates and a substantial gain in efficiency. + Hodges-Tompkins volatility is a bias correction for estimation using an overlapping data sample. + It produces unbiased estimates and a substantial gain in efficiency. - Rogers-Satchell is an estimator for measuring the volatility with an average return not equal to zero. - Unlike Parkinson and Garman-Klass estimators, Rogers-Satchell incorporates a drift term, - mean return not equal to zero. + Rogers-Satchell is an estimator for measuring the volatility with an average return not equal to zero. + Unlike Parkinson and Garman-Klass estimators, Rogers-Satchell incorporates a drift term, + mean return not equal to zero. - Yang-Zhang volatility is the combination of the overnight (close-to-open volatility). - It is a weighted average of the Rogers-Satchell volatility and the open-to-close volatility. - is_crypto : bool, optional - Whether the data is crypto or not. If True, volatility is calculated for 365 days instead of 252 + Yang-Zhang volatility is the combination of the overnight (close-to-open volatility). + It is a weighted average of the Rogers-Satchell volatility and the open-to-close volatility. + is_crypto : bool, optional + Whether the data is crypto or not. If True, volatility is calculated for 365 days instead of 252 - Returns - ------- - CommandOutput[List[Data]] - The cones data. + Returns + ------- + CommandOutput[List[Data]] + The cones data. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> cones_data = obb.ta.cones(data=stock_data.results, lower_q=0.25, upper_q=0.75, model="STD") - """ + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> cones_data = obb.ta.cones(data=stock_data.results, lower_q=0.25, upper_q=0.75, model="STD") + """ inputs = filter_inputs( data=data, index=index, @@ -1568,50 +1353,42 @@ class CLASS_ta(Container): @filter_call @validate_arguments(config=dict(arbitrary_types_allowed=True)) - def ema( - self, - data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], - target: str = "close", - index: str = "date", - length: int = 50, - offset: int = 0, - chart: bool = False, - ) -> CommandOutput[typing.List]: + def ema(self, data: Union[List[openbb_provider.abstract.data.Data], pandas.DataFrame], target: str = 'close', index: str = 'date', length: int = 50, offset: int = 0, chart: bool = False) -> CommandOutput[typing.List]: """ - The Exponential Moving Average is a staple of technical - analysis and is used in countless technical indicators. In a Simple Moving - Average, each value in the time period carries equal weight, and values outside - of the time period are not included in the average. However, the Exponential - Moving Average is a cumulative calculation, including all data. Past values have - a diminishing contribution to the average, while more recent values have a greater - contribution. This method allows the moving average to be more responsive to changes - in the data. + The Exponential Moving Average is a staple of technical + analysis and is used in countless technical indicators. In a Simple Moving + Average, each value in the time period carries equal weight, and values outside + of the time period are not included in the average. However, the Exponential + Moving Average is a cumulative calculation, including all data. Past values have + a diminishing contribution to the average, while more recent values have a greater + contribution. This method allows the moving average to be more responsive to changes + in the data. - Parameters - ---------- - data : List[Data] - The data to use for the calculation. - target : str - Target column name. - index : str, optional - Index column name to use with `data`, by default "date" - length : int, optional - The length of the calculation, by default 50. - offset : int, optional - The offset of the calculation, by default 0. + Parameters + ---------- + data : List[Data] + The data to use for the calculation. + target : str + Target column name. + index : str, optional + Index column name to use with `data`, by default "date" + length : int, optional + The length of the calculation, by default 50. + offset : int, optional + The offset of the calculation, by default 0. - Returns - ------- - CommandOutput[List[Data]] - The calculated data. + Returns + ------- + CommandOutput[List[Data]] + The calculated data. - Examples - -------- - >>> from openbb import obb - >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") - >>> ema_data = obb.ta.ema(data=stock_data.results,target="close",length=50,offset=0) + Examples + -------- + >>> from openbb import obb + >>> stock_data = obb.stocks.load(symbol="TSLA", start_date="2023-01-01", provider="fmp") + >>> ema_data = obb.ta.ema(data=stock_data.results,target="close",length=50,offset=0) - """ + """ inputs = filter_inputs( data=data, target=target, diff --git a/openbb_sdk/sdk/core/openbb_core/app/static/package_builder.py b/openbb_sdk/sdk/core/openbb_core/app/static/package_builder.py index 5be1fb330fb..de7e34f1952 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/static/package_builder.py +++ b/openbb_sdk/sdk/core/openbb_core/app/static/package_builder.py @@ -7,8 +7,6 @@ from inspect import Parameter, _empty, isclass, signature from json import dumps from pathlib import Path from typing import ( - Annotated, - Any, Callable, Dict, List, @@ -17,12 +15,13 @@ from typing import ( Type, Union, get_args, - get_origin, get_type_hints, ) import pandas as pd +from pydantic.fields import ModelField from starlette.routing import BaseRoute +from typing_extensions import _AnnotatedAlias from openbb_core.app.provider_interface import get_provider_interface from openbb_core.app.router import RouterLoader @@ -32,11 +31,11 @@ class PackageBuilder: """Build the extension package for the SDK.""" @classmethod - def build(cls, lint: bool = True) -> None: + def build(cls, modules: Optional[List[str]] = None, lint: bool = True) -> None: """Build the extensions for the SDK.""" print("\nBuilding extensions package...\n") cls.save_module_map() - cls.save_modules() + cls.save_modules(modules) cls.save_package() if lint: cls.run_linters() @@ -57,7 +56,7 @@ class PackageBuilder: ) @classmethod - def save_modules(cls): + def save_modules(cls, modules: Optional[List[str]] = None): """Save the modules.""" print("\nWriting modules...") route_map = PathHandler.build_route_map() @@ -69,6 +68,9 @@ class PackageBuilder: MAX_LEN = max([len(path) for path in path_list if path != "/"]) + if modules: + path_list = [path for path in path_list if path in modules] + for path in path_list: route = PathHandler.get_route(path=path, route_map=route_map) if route is None: @@ -106,8 +108,11 @@ class PackageBuilder: class ModuleBuilder: + """Build the module for the SDK.""" + @staticmethod def build(path: str) -> str: + """Build the module.""" code = "### THIS FILE IS AUTO-GENERATED. DO NOT EDIT. ###\n" code += ImportDefinition.build(path=path) code += ClassDefinition.build(path=path) @@ -116,8 +121,11 @@ class ModuleBuilder: class ImportDefinition: + """Build the import definition for the SDK.""" + @staticmethod def filter_hint_type_list(hint_type_list: List[Type]) -> List[Type]: + """Filter the hint type list.""" new_hint_type_list = [] for hint_type in hint_type_list: if hint_type != _empty and hint_type.__module__ != "builtins": @@ -129,6 +137,7 @@ class ImportDefinition: @classmethod def get_function_hint_type_list(cls, func: Callable) -> List[Type]: + """Get the hint type list from the function.""" sig = signature(func) parameter_map = sig.parameters return_type = sig.return_annotation @@ -148,6 +157,7 @@ class ImportDefinition: @classmethod def get_path_hint_type_list(cls, path: str) -> List[Type]: + """Get the hint type list from the path.""" route_map = PathHandler.build_route_map() path_list = PathHandler.build_path_list(route_map=route_map) child_path_list = PathHandler.get_child_path_list( @@ -165,6 +175,7 @@ class ImportDefinition: @classmethod def build(cls, path: str) -> str: + """Build the import definition.""" hint_type_list = cls.get_path_hint_type_list(path=path) code = "\nfrom openbb_core.app.static.container import Container" code += "\nfrom openbb_core.app.model.command_output import CommandOutput" @@ -175,7 +186,6 @@ class ImportDefinition: code += "\nimport openbb_provider" code += "\nimport pandas" code += "\nimport datetime" - code += "\nfrom types import NoneType" code += "\nimport pydantic" code += "\nfrom pydantic import validate_arguments" code += "\nfrom inspect import Parameter" @@ -195,8 +205,11 @@ class ImportDefinition: class ClassDefinition: + """Build the class definition for the SDK.""" + @staticmethod def build(path: str) -> str: + """Build the class definition.""" class_name = PathHandler.build_module_class(path=path) code = f"\nclass {class_name}(Container):\n" route_map = PathHandler.build_route_map() @@ -222,92 +235,131 @@ class ClassDefinition: class DocstringGenerator: - """Generate docstrings for the commands dynamically.""" + """Dynamically generate docstrings for the commands.""" @staticmethod - def get_docstrings(query_mapping: dict) -> dict: - """Get docstrings from the query mapping.""" - mapping = query_mapping.copy() - for _, model_mapping in mapping.items(): - for _, query_params_mapping in model_mapping.items(): - query_params_mapping.pop("fields", None) - return mapping + def get_command_output_description() -> str: + """Get the command output description.""" + command_output_description = ( + "\nReturns\n" + "-------\n" + "CommandOutput\n" + " results: List[Data]\n" + " Serializable results.\n" + " provider: Optional[PROVIDERS]\n" + " Provider name.\n" + " warnings: Optional[List[Warning_]]\n" + " List of warnings.\n" + " error: Optional[Error]\n" + " Caught exceptions.\n" + " chart: Optional[Chart]\n" + " Chart object.\n" + ) + + return command_output_description @staticmethod - def clean_provider_docstring( - section_name: Literal["QueryParams", "Data"], docstring: str, model_name: str - ) -> str: - """Clean the provider docstring from standard fields.""" - provider_interface = get_provider_interface() - if section_name == "QueryParams": - standard_fields = provider_interface.params[model_name][ - "standard" - ].__dataclass_fields__.keys() - elif section_name == "Data": - standard_fields = provider_interface.data[model_name][ - "standard" - ].__dataclass_fields__.keys() + def get_available_providers(query_mapping: dict) -> str: + """Return a string of available providers.""" + available_providers = ", ".join(query_mapping.keys()) + available_providers = available_providers.replace("openbb, ", "") + available_providers = available_providers.replace("openbb", "") + + provider_string = f"provider: Literal[{available_providers}]\n" + provider_string += " The provider to use for the query.\n" + + return provider_string + + @staticmethod + def reorder_dictionary(dictionary: dict, key_to_move_first: str) -> dict: + """Reorder a dictionary so that a given key is first.""" + if key_to_move_first in dictionary: + ordered_dict = OrderedDict( + [(key_to_move_first, dictionary[key_to_move_first])] + ) + + for key, value in dictionary.items(): + if key != key_to_move_first: + ordered_dict[key] = value + + return ordered_dict else: - return docstring + return dictionary - doc_lines = docstring.split("\n") - cleaned_lines = [] - skip_lines = False + @classmethod + def extract_field_details( + cls, + model_name: str, + provider: str, + section_name: str, + section_docstring: str, + mapping: dict, + ) -> str: + """Extract the field details from the map and add them to the docstring.""" + if section_docstring == "": + return section_docstring - for line in doc_lines: - stripped_line = line.strip() - parameter_word = stripped_line.split(" : ")[0] + padding = " " - if parameter_word in standard_fields: - skip_lines = True - elif ":" in stripped_line and parameter_word not in standard_fields: - skip_lines = False + field_mapping = mapping[model_name][provider.lower()][section_name]["fields"] + fields = field_mapping.keys() - if not skip_lines: - cleaned_lines.append(line) + if len(fields) == 0: + section_docstring += "All fields are standardized.\n" + else: + for field in fields: + # We need to get the string representation of the field type + # because Pydantic uses a custom repr. + try: + field_type = field_mapping[field].__repr_args__()[1][1] + except AttributeError: + # Fallback to the annotation if the repr fails + field_type = field_mapping[field].annotation - for i, line in enumerate(cleaned_lines): - try: - if line == "---------" and cleaned_lines[i + 1] == " ": - cleaned_lines[i] = "---------" - cleaned_lines[i + 1] = "All fields are standardized.\n" - break - except IndexError: - cleaned_lines.append("All fields are standardized.\n") + field_description = field_mapping[field].field_info.description - return "\n".join(cleaned_lines) + section_docstring += f"{field} : {field_type}\n" + section_docstring += f"{padding}{field_description}\n" + + if provider == "openbb" and section_name == "QueryParams": + section_docstring += cls.get_command_output_description() + + return section_docstring @classmethod def generate_provider_docstrings( - cls, docstring: str, docstring_mapping: dict, model_name: str + cls, + docstring: str, + query_mapping: dict, + model_name: str, + provider_interface_mapping: dict, ) -> str: """Generate the docstring for the provider.""" - for provider, model_mapping in docstring_mapping.items(): + for provider, model_mapping in query_mapping.items(): docstring += f"\n{provider}" docstring += f"\n{'=' * len(provider)}" - for section_name, section_docstring in model_mapping.items(): - missing_doc = "\nReturns\n-------\nDocumentation not available.\n\n" - section_docstring = ( - section_docstring["docstring"] - if section_docstring["docstring"] - else missing_doc - ) - # clean the docstring from its original indentation - if missing_doc != section_docstring: - section_docstring = "\n".join( - line[4:] for line in section_docstring.split("\n")[1:] - ) - section_docstring = "\n".join( - f"{line}" for line in section_docstring.split("\n") - ) - - if provider != "Standard": - section_docstring = cls.clean_provider_docstring( - section_name, - docstring=section_docstring, - model_name=model_name, + for section_name in model_mapping: + section_docstring = "" + if section_name == "QueryParams": + section_docstring += "\nParameters\n----------\n" + if provider == "openbb": + section_docstring += ( + f"{cls.get_available_providers(query_mapping)}" ) + elif section_name == "Data": + underline = "-" * len(model_name) + section_docstring += f"\n{model_name}\n{underline}\n" + else: + continue + + section_docstring = cls.extract_field_details( + model_name=model_name, + provider=provider, + section_name=section_name, + section_docstring=section_docstring, + mapping=provider_interface_mapping, + ) docstring += f"\n{section_docstring}" @@ -319,23 +371,15 @@ class DocstringGenerator: provider_interface_mapping = get_provider_interface().map query_mapping = provider_interface_mapping.get(model_name, None) if query_mapping: - docstring_mapping = cls.get_docstrings(query_mapping) - docstring = func.__doc__ or "" + docstring += "\n\n" - available_providers = ", ".join(docstring_mapping.keys()) - available_providers = available_providers.replace("openbb, ", "") - available_providers = available_providers.replace("openbb", "") - - docstring += f"\n\nAvailable providers: {available_providers}\n" - - docstring_mapping_ordered = { - "Standard": docstring_mapping.pop("openbb", None), # type: ignore - **docstring_mapping, - } - + query_mapping_ordered = cls.reorder_dictionary(query_mapping, "openbb") docstring = cls.generate_provider_docstrings( - docstring, docstring_mapping_ordered, model_name + docstring=docstring, + query_mapping=query_mapping_ordered, + model_name=model_name, + provider_interface_mapping=provider_interface_mapping, ) func.__doc__ = docstring @@ -343,8 +387,11 @@ class DocstringGenerator: class MethodDefinition: + """Build the method definition for the SDK.""" + @staticmethod def build_class_loader_method(path: str) -> str: + """Build the class loader method.""" module_name = PathHandler.build_module_name(path=path) class_name = PathHandler.build_module_class(path=path) function_name = path.rsplit("/", maxsplit=1)[-1].strip("/") @@ -357,7 +404,8 @@ class MethodDefinition: return code @staticmethod - def get_type(field: Any) -> type: + def get_type(field: ModelField) -> type: + """Get the type of the field.""" field_type = getattr(field, "type", Parameter.empty) if isclass(field_type): name = field_type.__name__ @@ -368,7 +416,8 @@ class MethodDefinition: return field_type @staticmethod - def get_default(field: Any): + def get_default(field: ModelField): + """Get the default value of the field.""" field_default = getattr(field, "default", None) if field_default is None or field_default is MISSING: return Parameter.empty @@ -381,12 +430,14 @@ class MethodDefinition: @staticmethod def is_annotated_dc(annotation) -> bool: - return get_origin(annotation) == Annotated and hasattr( + """Check if the annotation is an annotated dataclass.""" + return type(annotation) is _AnnotatedAlias and hasattr( annotation.__args__[0], "__dataclass_fields__" ) @staticmethod - def reorder_params(params: Dict[str, Parameter]) -> OrderedDict[str, Parameter]: + def reorder_params(params: Dict[str, Parameter]) -> "OrderedDict[str, Parameter]": + """Reorder the params.""" formatted_keys = list(params.keys()) for k in ["provider", "extra_params"]: if k in formatted_keys: @@ -402,11 +453,12 @@ class MethodDefinition: @staticmethod def format_params( parameter_map: Dict[str, Parameter] - ) -> OrderedDict[str, Parameter]: + ) -> "OrderedDict[str, Parameter]": + """Format the params.""" # These are types we want to expand. # For example, start_date is always a 'date', but we also accept 'str' as input. # Be careful, if the type is not coercible by pydantic to the original type, you - # will need to had some conversion code to the method implementation. + # will need to add some conversion code in the input filter. TYPE_EXPANSION = { "data": pd.DataFrame, "start_date": str, @@ -466,6 +518,7 @@ class MethodDefinition: @staticmethod def build_func_params(parameter_map: Dict[str, Parameter]) -> str: + """Build the function parameters.""" od = MethodDefinition.format_params(parameter_map=parameter_map) func_params = ", ".join(str(param) for param in od.values()) func_params = func_params.replace("NoneType", "None") @@ -477,6 +530,7 @@ class MethodDefinition: @staticmethod def build_func_returns(return_type: type) -> str: + """Build the function returns.""" if return_type == _empty: func_returns = "None" elif return_type.__module__ == "builtins": @@ -490,9 +544,13 @@ class MethodDefinition: # select = f"[{inner_type.__module__}.{inner_type.__name__}]" # func_returns = f"CommandOutput[{item_type.__module__}.{item_type.__name__}[{select}]]" else: - # inner_type = getattr(item_type, "__name__", "_name") + inner_type_name = ( + item_type.__name__ + if hasattr(item_type, "__name__") + else item_type._name + ) func_returns = ( - f"CommandOutput[{item_type.__module__}.{item_type.__name__}]" + f"CommandOutput[{item_type.__module__}.{inner_type_name}]" ) return func_returns @@ -501,6 +559,7 @@ class MethodDefinition: def build_command_method_signature( func_name: str, parameter_map: Dict[str, Parameter], return_type: type ) -> str: + """Build the command method signature.""" func_params = MethodDefinition.build_func_params(parameter_map) func_returns = MethodDefinition.build_func_returns(return_type) code = "\n @filter_call" @@ -517,12 +576,14 @@ class MethodDefinition: @staticmethod def build_command_method_doc(func: Callable): + """Build the command method docstring.""" code = f' """{func.__doc__}"""\n' if func.__doc__ else "" return code @staticmethod def build_command_method_implementation(path: str, func: Callable): + """Build the command method implementation.""" sig = signature(func) parameter_map = dict(sig.parameters) parameter_map.pop("cc", None) @@ -560,6 +621,7 @@ class MethodDefinition: def build_command_method( cls, path: str, func: Callable, model_name: Optional[str] ) -> str: + """Build the command method.""" func_name = func.__name__ sig = signature(func) @@ -582,8 +644,11 @@ class MethodDefinition: class PathHandler: + """Handle the paths for the SDK.""" + @staticmethod def build_route_map() -> Dict[str, BaseRoute]: + """Build the route map.""" router = RouterLoader.from_extensions() route_map = {route.path: route for route in router.api_router.routes} # type: ignore @@ -591,6 +656,7 @@ class PathHandler: @staticmethod def build_path_list(route_map: Dict[str, BaseRoute]) -> List[str]: + """Build the path list.""" path_list = [] for route_path in route_map: if route_path not in path_list: @@ -607,10 +673,12 @@ class PathHandler: @staticmethod def get_route(path: str, route_map: Dict[str, BaseRoute]): + """Get the route from the path.""" return route_map.get(path, None) @staticmethod def get_child_path_list(path: str, path_list: List[str]) -> List[str]: + """Get the child path list.""" direct_children = [] for p in path_list: if p.startswith(path): @@ -622,28 +690,34 @@ class PathHandler: @staticmethod def clean_path(path: str) -> str: + """Clean the path.""" if path.startswith("/"): path = path[1:] return path.replace("-", "_").replace("/", "_") @classmethod def build_module_name(cls, path: str) -> str: + """Build the module name.""" if path == "": - return "__commands__" + return "__extensions__" return cls.clean_path(path=path) @classmethod def build_module_class(cls, path: str) -> str: + """Build the module class.""" if path == "": - return "Commands" + return "Extensions" return f"CLASS_{cls.clean_path(path=path)}" class Linters: + """Run the linters for the SDK.""" + current_folder = str(Path(Path(__file__).parent, "package").absolute()) @staticmethod def print_separator(symbol: str, length: int = 160): + """Print a separator.""" print(symbol * length) @staticmethod @@ -665,12 +739,15 @@ class Linters: @classmethod def black(cls): + """Run black.""" cls.run(linter="black") @classmethod def ruff(cls): + """Run ruff.""" cls.run(linter="ruff", flags=["--fix"]) @classmethod def mypy(cls): + """Run mypy.""" cls.run(linter="mypy", flags=["--ignore-missing-imports"]) diff --git a/openbb_sdk/sdk/core/openbb_core/app/utils.py b/openbb_sdk/sdk/core/openbb_core/app/utils.py index 03ab2f84ac6..14f8874539e 100644 --- a/openbb_sdk/sdk/core/openbb_core/app/utils.py +++ b/openbb_sdk/sdk/core/openbb_core/app/utils.py @@ -4,11 +4,19 @@ import pandas as pd from openbb_provider.abstract.data import Data -def basemodel_to_df(data: List[Data], index: Optional[str] = None) -> pd.DataFrame: +def basemodel_to_df( + data: Union[List[Data], Data], index: Optional[str] = None +) -> pd.DataFrame: """Convert list of BaseModel to a Pandas DataFrame.""" - df = pd.DataFrame([d.dict() for d in data]) + + data_list = [data] if not isinstance(data, list) else data + + df = pd.DataFrame([d.dict() for d in data_list]) if index and index in df.columns: df = df.set_index(index) + if df.index.name == "date": + df.index = pd.to_datetime(df.index) + df.sort_index(axis=0, inplace=True) return df diff --git a/openbb_sdk/sdk/core/tests/__init__.py b/openbb_sdk/sdk/core/tests/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/api/dependency/test_system.py b/openbb_sdk/sdk/core/tests/api/dependency/test_system.py new file mode 100644 index 00000000000..11e08d06838 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/api/dependency/test_system.py @@ -0,0 +1,34 @@ +"""Test the system module.""" +import asyncio +from unittest.mock import MagicMock, patch + +from openbb_core.api.dependency.system import ( + SystemSettings, + get_system_service, + get_system_settings, +) + + +@patch("openbb_core.api.dependency.system.SystemService") +def test_get_system_settings(mock_system_service): + """Test get_system_settings.""" + mock_system_settings = MagicMock(spec=SystemSettings) + mock_system_service.system_settings = mock_system_settings + + with patch( + "openbb_core.api.dependency.system.__system_service", new=mock_system_service + ): + system_settings = asyncio.run(get_system_settings()) + + assert system_settings == mock_system_settings + + +@patch("openbb_core.api.dependency.system.SystemService") +def test_get_system_service(mock_system_service): + """Test get_system_service.""" + with patch( + "openbb_core.api.dependency.system.__system_service", new=mock_system_service + ): + system_service = asyncio.run(get_system_service()) + + assert system_service == mock_system_service diff --git a/openbb_sdk/sdk/core/tests/api/dependency/test_user.py b/openbb_sdk/sdk/core/tests/api/dependency/test_user.py new file mode 100644 index 00000000000..091e0d4f07d --- /dev/null +++ b/openbb_sdk/sdk/core/tests/api/dependency/test_user.py @@ -0,0 +1,80 @@ +"""Test the user module.""" +import asyncio +from unittest.mock import MagicMock, patch + +from openbb_core.api.dependency.user import ( + AccessToken, + SystemSettings, + UserSettings, + authenticate_user, + create_access_token, + create_jwt_token, + get_password_hash, + get_user, + get_user_service, + verify_password, +) + + +def test_password_functions(): + """Test password functions.""" + password = "my_password" + hashed_password = get_password_hash(password) + assert verify_password(password, hashed_password) + + +@patch("openbb_core.api.dependency.user.UserService") +def test_authenticate_user(mock_user_service): + """Test authenticate_user.""" + mock_user_settings = MagicMock( + spec=UserSettings, profile=MagicMock(password_hash="hashed_password") + ) + mock_user_service.user_settings_repository.read_by_profile.return_value = ( + mock_user_settings + ) + with patch("openbb_core.api.dependency.user.verify_password", return_value=True): + result = authenticate_user("password", "username", mock_user_service) + + assert result == mock_user_settings + + +def test_create_access_token(): + """Test create_access_token.""" + sub = "subject" + access_token = create_access_token(sub) + + assert isinstance(access_token, AccessToken) + assert access_token.sub == sub + + +def test_create_jwt_token(): + """Test create_jwt_token.""" + mock_access_token = MagicMock( + spec=AccessToken, dict=lambda: {"sub": "subject", "exp": 12345} + ) + jwt_token = create_jwt_token(mock_access_token) + + assert isinstance(jwt_token, str) + + +@patch("openbb_core.api.dependency.user.UserService") +def test_get_user_service(mock_user_service): + """Test get_user_service.""" + mock_system_settings = MagicMock(spec=SystemSettings, dbms_uri=None) + with patch("openbb_core.api.dependency.user.__user_service", new=None): + asyncio.run(get_user_service(mock_system_settings)) + + mock_user_service.assert_called_once_with() + + +@patch("openbb_core.api.dependency.user.jwt.decode") +@patch("openbb_core.api.dependency.user.UserService") +def test_get_user(mock_user_service, mock_decode): + """Test get_user.""" + mock_decode.return_value = {"sub": "user_id"} + mock_user_settings = MagicMock(spec=UserSettings, profile=MagicMock(active=True)) + mock_user_service.user_settings_repository.read.return_value = mock_user_settings + with patch("openbb_core.api.dependency.user.__user_service", new=mock_user_service): + result = asyncio.run(get_user("jwt_token", mock_user_service)) + + assert result == mock_user_settings diff --git a/openbb_sdk/sdk/core/tests/api/router/test_router_coverage.py b/openbb_sdk/sdk/core/tests/api/router/test_router_coverage.py new file mode 100644 index 00000000000..38738080b20 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/api/router/test_router_coverage.py @@ -0,0 +1,29 @@ +"""Test coverage for the router module.""" + + +from unittest.mock import patch + +from openbb_core.api.router.coverage import get_command_coverage, get_provider_coverage + + +@patch("openbb_core.api.router.coverage.CommandMap") +def test_get_provider_coverage(mock_command_map): + """Test get provider coverage.""" + mock_command_map.return_value.provider_coverage = { + "provider1": ["coverage1", "coverage2"] + } + + response = get_provider_coverage() + + assert response + + +@patch("openbb_core.api.router.coverage.CommandMap") +def test_get_command_coverage(mock_command_map): + """Test get command coverage.""" + mock_command_map.return_value.command_coverage = { + "command1": ["coverage1", "coverage2"] + } + + response = get_command_coverage() + assert response diff --git a/openbb_sdk/sdk/core/tests/api/router/test_router_system.py b/openbb_sdk/sdk/core/tests/api/router/test_router_system.py new file mode 100644 index 00000000000..1f916f70ab2 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/api/router/test_router_system.py @@ -0,0 +1,18 @@ +"""Test the router system module.""" + +import asyncio +from unittest.mock import Mock, patch + +from openbb_core.api.router.system import get_system_model +from openbb_core.app.model.system_settings import SystemSettings + + +@patch("openbb_core.api.router.system.get_system_settings") +def test_get_system_model(mock_get_system_settings): + """Test get system model.""" + mock_system_settings = Mock(spec=SystemSettings) + mock_get_system_settings.return_value = mock_system_settings + + response = asyncio.run(get_system_model(system_settings=mock_system_settings)) + + assert response == mock_system_settings diff --git a/openbb_sdk/sdk/core/tests/api/router/test_settings.py b/openbb_sdk/sdk/core/tests/api/router/test_settings.py new file mode 100644 index 00000000000..f7d48b8b21f --- /dev/null +++ b/openbb_sdk/sdk/core/tests/api/router/test_settings.py @@ -0,0 +1,33 @@ +"""Test the router settings.py module.""" +import asyncio +from unittest.mock import Mock + +from openbb_core.api.router.settings import patch_user_credentials, read_users_settings +from openbb_core.app.model.credentials import Credentials +from openbb_core.app.model.user_settings import UserSettings + + +def test_read_users_settings(): + """Test read users settings.""" + mock_user_settings = Mock() + + result = asyncio.run(read_users_settings(user_settings=mock_user_settings)) + + assert result == mock_user_settings + + +def test_patch_user_credentials(): + """Test patch user credentials.""" + credentials = Credentials(username="testuser", password="testpass") + user_settings = UserSettings(credentials=credentials) + + mock_user_service = Mock() + + result = asyncio.run( + patch_user_credentials(credentials, user_settings, mock_user_service) + ) + + assert result == user_settings + mock_user_service.user_settings_repository.update.assert_called_once_with( + user_settings + ) diff --git a/openbb_sdk/sdk/core/tests/app/logs/formatters/test_formatter_with_exceptions.py b/openbb_sdk/sdk/core/tests/app/logs/formatters/test_formatter_with_exceptions.py new file mode 100644 index 00000000000..613d8e521aa --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/logs/formatters/test_formatter_with_exceptions.py @@ -0,0 +1,308 @@ +import logging +import os +from unittest.mock import Mock + +import pytest +from openbb_core.app.logs.formatters.formatter_with_exceptions import ( + FormatterWithExceptions, +) + +logging_settings = Mock() +logging_settings.app_name = "test_app_name" +logging_settings.app_id = "test_app_id" +logging_settings.session_id = "test_session_id" +logging_settings.commit_hash = "test_commit_hash" +logging_settings.user_id = "test_user_id" + + +@pytest.fixture +def formatter(): + return FormatterWithExceptions(logging_settings) + + +# Test when exc_text is not empty (should return "X") +def test_level_name_with_exc_text(formatter): + record = logging.LogRecord( + name="test_logger", + level=logging.INFO, + pathname="", + lineno=0, + msg="", + args=None, + exc_info=None, + func=None, + ) + record.exc_text = "Exception occurred!" + assert formatter.calculate_level_name(record) == "X" + + +# Test when levelname is not empty (should return the first character of levelname) +def test_level_name_with_levelname(formatter): + record = logging.LogRecord( + name="test_logger", + level=logging.WARNING, + pathname="", + lineno=0, + msg="", + args=None, + exc_info=None, + func=None, + ) + record.levelname = "WARNING" + assert formatter.calculate_level_name(record) == "W" + + +# Test when func_name_override and session_id are present in the record +def test_extract_log_extra_with_override_and_session_id(formatter): + record = logging.LogRecord( + name="test_logger", + level=logging.INFO, + pathname="", + lineno=0, + msg="", + args=None, + exc_info=None, + func=None, + ) + record.func_name_override = "custom_function" + record.session_id = "1234567890" + + log_extra = formatter.extract_log_extra(record) + + assert log_extra == { + "sessionId": "1234567890", + } + + +# Test when only func_name_override is present in the record +def test_extract_log_extra_with_override(formatter): + record = logging.LogRecord( + name="test_logger", + level=logging.INFO, + pathname="", + lineno=0, + msg="", + args=None, + exc_info=None, + func=None, + ) + record.func_name_override = "custom_function" + + log_extra = formatter.extract_log_extra(record) + + assert log_extra == {} + assert record.funcName == "custom_function" + assert record.lineno == 0 + + +# Test when only session_id is present in the record +def test_extract_log_extra_with_session_id(formatter): + record = logging.LogRecord( + name="test_logger", + level=logging.INFO, + pathname="", + lineno=0, + msg="", + args=None, + exc_info=None, + func=None, + ) + record.session_id = "1234567890" + + log_extra = formatter.extract_log_extra(record) + + assert log_extra == { + "sessionId": "1234567890", + } + + +# Test when neither func_name_override nor session_id are present in the record +def test_extract_log_extra_with_no_override_or_session_id(formatter): + record = logging.LogRecord( + name="test_logger", + level=logging.INFO, + pathname="", + lineno=0, + msg="", + args=None, + exc_info=None, + func=None, + ) + + log_extra = formatter.extract_log_extra(record) + + assert log_extra == {} + + +@pytest.mark.parametrize( + "input_text, expected_output", + [ + ("Hello, this is 192.168.1.1", "Hello, this is FILTERED_IP "), + ("IP address: 10.0.0.1", "IP address: FILTERED_IP "), + ("No IPs here!", "No IPs here!"), + ("Another IP: 172.16.32.1", "Another IP: FILTERED_IP "), + ("1.2.3.4", " FILTERED_IP "), + ], +) +def test_mock_ipv4(input_text, expected_output, formatter): + assert formatter.mock_ipv4(input_text) == expected_output + + +@pytest.mark.parametrize( + "input_text, expected_output", + [ + ("Send an email to john@example.com", "Send an email to FILTERED_EMAIL "), + ("No emails here!", "No emails here!"), + ("Another email: alice@example.co.uk", "Another email: FILTERED_EMAIL "), + ("Contact us: support@example.net", "Contact us: FILTERED_EMAIL "), + ("myemail@example.com 12345", " FILTERED_EMAIL 12345"), + ], +) +def test_mock_email(input_text, expected_output, formatter): + assert formatter.mock_email(input_text) == expected_output + + +@pytest.mark.parametrize( + "input_text, expected_output", + [ + ('{"password": "secure_pass_123"}', '{"password": " FILTERED_PASSWORD "}'), + ('{"password": "p@$$w0rd!"}', '{"password": " FILTERED_PASSWORD "}'), + ('{"password": "12345"}', '{"password": " FILTERED_PASSWORD "}'), + ('{"password": "abc"}', '{"password": " FILTERED_PASSWORD "}'), + ( + '{"password": "my_password"} {"password": "test123"}', + '{"password": " FILTERED_PASSWORD "} {"password": " FILTERED_PASSWORD "}', + ), + ], +) +def test_mock_password(input_text, expected_output, formatter): + assert formatter.mock_password(input_text) == expected_output + + +@pytest.mark.parametrize( + "input_text, expected_output", + [ + ('{"FLAIR": "[python]"}', '{"FLAIR": "[ FILTERED_FLAIR ]"}'), + ('{"FLAIR": "[question]"}', '{"FLAIR": "[ FILTERED_FLAIR ]"}'), + ('{"FLAIR": "[bug]"}', '{"FLAIR": "[ FILTERED_FLAIR ]"}'), + ('{"FLAIR": "[feature]"}', '{"FLAIR": "[ FILTERED_FLAIR ]"}'), + ('{"FLAIR": "[announcement]"}', '{"FLAIR": "[ FILTERED_FLAIR ]"}'), + ('{"FLAIR": "[FILTERED_FLAIR]"}', '{"FLAIR": "[ FILTERED_FLAIR ]"}'), + ], +) +def test_mock_flair(input_text, expected_output, formatter): + assert formatter.mock_flair(input_text) == expected_output + + +@pytest.mark.parametrize( + "input_text, expected_output", + [ + ( + "This is C:\\Users\\username\\file.txt", + "This is C:\\Users\\username\\file.txt", + ), + ("No home directory here!", "No home directory here!"), + ( + f"This is {os.path.expanduser('~')}\\file.txt", + "This is MOCKING_USER_PATH\\file.txt", + ), + ( + f"Some path: {os.path.expanduser('~/subfolder')}", + "Some path: MOCKING_USER_PATH/subfolder", + ), + ], +) +def test_mock_home_directory(input_text, expected_output, formatter): + assert formatter.mock_home_directory(input_text) == expected_output + + +@pytest.mark.parametrize( + "input_text, expected_output", + [ + ( + "This is a string with a\nnewline", + "This is a string with a MOCKING_BREAKLINE newline", + ), + ("'Traceback occurred", "Traceback occurred"), + ("No newlines or 'Traceback'", "No newlines or Traceback'"), + ("'Traceback in the\nmiddle'", "Traceback in the MOCKING_BREAKLINE middle'"), + ( + "Multiple\n\nnewlines", + "Multiple MOCKING_BREAKLINE MOCKING_BREAKLINE newlines", + ), + ], +) +def test_filter_special_tags(input_text, expected_output, formatter): + assert formatter.filter_special_tags(input_text) == expected_output + + +@pytest.mark.parametrize( + "input_text, expected_output", + [ + ( + f"This is {os.path.expanduser('~')}\\file.txt", + "This is MOCKING_USER_PATH\\file.txt", + ), + ('{"FLAIR": "[announcement]"}', '{"FLAIR": "[ FILTERED_FLAIR ]"}'), + ("Another email: alice@example.co.uk", "Another email: FILTERED_EMAIL "), + ("Another IP: 172.16.32.1", "Another IP: FILTERED_IP "), + ('{"password": "secure_pass_123"}', '{"password": " FILTERED_PASSWORD "}'), + ], +) +def test_filter_piis(input_text, expected_output, formatter): + assert formatter.filter_piis(input_text) == expected_output + + +@pytest.mark.parametrize( + "input_text, expected_output", + [ + ( + f"This is {os.path.expanduser('~')}\\file.txt", + "This is MOCKING_USER_PATH\\file.txt", + ), + ('{"FLAIR": "[announcement]"}', '{"FLAIR": "[ FILTERED_FLAIR ]"}'), + ("Another email: alice@example.co.uk", "Another email: FILTERED_EMAIL "), + ("Another IP: 172.16.32.1", "Another IP: FILTERED_IP "), + ('{"password": "secure_pass_123"}', '{"password": " FILTERED_PASSWORD "}'), + ("'Traceback in the\nmiddle'", "Traceback in the MOCKING_BREAKLINE middle'"), + ], +) +def test_filter_log_line(input_text, expected_output, formatter): + assert formatter.filter_log_line(input_text) == expected_output + + +def test_formatException_invalid(): + with pytest.raises(Exception): + formatter.formatException(Exception("Big bad error")) + + +def test_format(formatter): + # Prepare the log record + log_record = logging.LogRecord( + name="test_logger", + level=logging.INFO, + pathname="/path/to/module.py", + lineno=42, + msg="This is a test log message with | symbol", + args=(), + exc_info=None, + ) + + # Set up the mock objects + formatter.calculate_level_name = Mock(return_value="INFO") + formatter.filter_log_line = Mock(return_value="Filtered log message") + + # Call the format method + formatted_log = formatter.format(log_record) + + # Assertions + expected_log = "INFO|test_app_name|test_commit_hash|test_app_id|test_session_id|test_user_id|Filtered log message" + + print(expected_log) + print(formatted_log) + + assert formatted_log == expected_log + + # Check if the mock methods were called + formatter.calculate_level_name.assert_called_once() + formatter.filter_log_line.assert_called_once() diff --git a/openbb_sdk/sdk/core/tests/app/logs/handlers/test_path_tracking_file_handler.py b/openbb_sdk/sdk/core/tests/app/logs/handlers/test_path_tracking_file_handler.py new file mode 100644 index 00000000000..ea53d2e3dd5 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/logs/handlers/test_path_tracking_file_handler.py @@ -0,0 +1,64 @@ +from pathlib import Path +from unittest.mock import MagicMock, patch + +import pytest +from openbb_core.app.logs.handlers.path_tracking_file_handler import ( + PathTrackingFileHandler, +) + + +class MockLoggingSettings: + def __init__(self, app_name, user_logs_directory, session_id, frequency): + self.app_name = app_name + self.user_logs_directory = Path(user_logs_directory) + self.session_id = session_id + self.frequency = frequency + + +logging_settings = MagicMock(spec=MockLoggingSettings) +logging_settings.app_name = "test_app_name_xpto" +logging_settings.user_logs_directory = MagicMock() +logging_settings.user_logs_directory.absolute.return_value = Path( + "/mocked/logs/directory" +) +logging_settings.session_id = "test_session_id" +logging_settings.frequency = "H" + + +@pytest.fixture +def handler(): + # patch `pathlib.Path.joinpath` to return a string containing the joined path + with patch.object(Path, "joinpath", return_value=Path("mocked_path")): + return PathTrackingFileHandler(logging_settings) + + +def test_build_log_file_path(handler): + # Define a sample LoggingSettings object with mock attributes + settings = MagicMock(spec=MockLoggingSettings) + settings.app_name = "my_app" + settings.user_logs_directory = MagicMock() + settings.user_logs_directory.absolute.return_value = Path("/mocked/logs/directory") + settings.session_id = "abc123" + + # patch `pathlib.Path.joinpath` to return a string containing the joined path + with patch.object( + Path, "joinpath", return_value=Path("mocked_path") + ) as mock_joinpath: + result_path = handler.build_log_file_path(settings) + + # Assert the result is correct + assert result_path == Path("mocked_path") + + mock_joinpath.assert_called_once_with("my_app_abc123") + + +def test_clean_expired_files(handler): + with patch( + "openbb_core.app.logs.handlers.path_tracking_file_handler.get_expired_file_list" + ) as mock_get_expired_file_list, patch( + "openbb_core.app.logs.handlers.path_tracking_file_handler.remove_file_list" + ) as mock_remove_file_list: + handler.clean_expired_files(123) + + assert mock_get_expired_file_list.call_count == 3 + assert mock_remove_file_list.call_count == 3 diff --git a/openbb_sdk/sdk/core/tests/app/logs/handlers/test_posthog_handler.py b/openbb_sdk/sdk/core/tests/app/logs/handlers/test_posthog_handler.py new file mode 100644 index 00000000000..704c44b803d --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/logs/handlers/test_posthog_handler.py @@ -0,0 +1,225 @@ +import logging +from pathlib import Path +from unittest.mock import MagicMock + +import pytest +from openbb_core.app.logs.handlers.posthog_handler import ( + PosthogHandler, +) + + +class MockLoggingSettings: + def __init__( + self, + app_name, + user_logs_directory, + session_id, + frequency, + appid, + commit_hash, + platform, + python_version, + terminal_version, + branch, + userid, + ): + self.app_name = app_name + self.user_logs_directory = Path(user_logs_directory) + self.session_id = session_id + self.frequency = frequency + self.app_id = appid + self.commit_hash = commit_hash + self.platform = platform + self.python_version = python_version + self.terminal_version = terminal_version + self.branch = branch + self.user_id = userid + + +logging_settings = MagicMock(spec=MockLoggingSettings) +logging_settings.app_name = "TestApp" +logging_settings.user_logs_directory = MagicMock() +logging_settings.user_logs_directory.absolute.return_value = Path( + "/mocked/logs/directory" +) +logging_settings.session_id = "session123" +logging_settings.frequency = "H" +logging_settings.app_id = "test123" +logging_settings.commit_hash = "commit123" +logging_settings.platform = "Windows" +logging_settings.python_version = "3.9" +logging_settings.terminal_version = "1.2.3" +logging_settings.branch = "main" +logging_settings.user_id = "user123" + + +@pytest.fixture +def handler(): + return PosthogHandler(logging_settings) + + +def test_emit_calls_send(mocker, handler): + # Arrange + record = logging.LogRecord( + name="test_logger", + level=logging.INFO, + pathname="test.py", + lineno=42, + msg="Test message", + args=None, + exc_info=None, + ) + + # Mock the send method + handler.send = mocker.MagicMock() + + # Act + handler.emit(record) + + # Assert + handler.send.assert_called_once_with(record=record) + + +def test_emit_calls_handleError_when_send_raises_exception(mocker, handler): + # Arrange + record = logging.LogRecord( + name="test_logger", + level=logging.ERROR, + pathname="test.py", + lineno=42, + msg="Test error message", + args=None, + exc_info=None, + ) + + # Mock the send method to raise an exception + handler.send = mocker.MagicMock(side_effect=Exception) + + # Mock the handleError method + handler.handleError = mocker.MagicMock() + + # Act + handler.emit(record) + + # Assert + handler.send.assert_called_once_with(record=record) + handler.handleError.assert_called_once_with(record) + + +def test_emit_calls_handleError_when_send_raises_exception_of_specific_type( + mocker, handler +): + # Arrange + record = logging.LogRecord( + name="test_logger", + level=logging.ERROR, + pathname="test.py", + lineno=42, + msg="Test error message", + args=None, + exc_info=None, + ) + + # Mock the send method to raise an exception of a specific type + handler.send = mocker.MagicMock(side_effect=ValueError) + + # Mock the handleError method + handler.handleError = mocker.MagicMock() + + # Act + handler.emit(record) + + # Assert + handler.send.assert_called_once_with(record=record) + handler.handleError.assert_called_once_with(record) + + +def test_emit_calls_handleError_when_send_raises_exception_of_another_type( + mocker, handler +): + # Arrange + record = logging.LogRecord( + name="test_logger", + level=logging.ERROR, + pathname="test.py", + lineno=42, + msg="Test error message", + args=None, + exc_info=None, + ) + + # Mock the send method to raise an exception of another type + handler.send = mocker.MagicMock(side_effect=TypeError) + + # Mock the handleError method + handler.handleError = mocker.MagicMock() + + # Act + handler.emit(record) + + # Assert + handler.send.assert_called_once_with(record=record) + handler.handleError.assert_called_once_with(record) + + +@pytest.mark.parametrize( + "log_info, expected_dict", + [ + ( + 'STARTUP: {"status": "success"}', + {"STARTUP": {"status": "success"}}, + ), + ( + '{"INPUT": {"something_something":"something_something"}}', + { + "SDK": {"INPUT": {"something_something": "something_something"}}, + }, + ), + ( + 'CMD: {"path": "/stocks/", "known_cmd": "load", "other_args": "aapl", "input": "load aapl"}', + { + "CMD": { + "path": "/stocks/", + "known_cmd": "load", + "other_args": "aapl", + "input": "load aapl", + } + }, + ), + ], +) +def test_log_to_dict(handler, log_info, expected_dict): + # Act + result = handler.log_to_dict(log_info) + + # Assert + assert result == expected_dict + + +@pytest.mark.parametrize( + "record, expected_extra", + [ + ( + logging.LogRecord( + "name", logging.INFO, "pathname", 42, "message", (), None, None + ), + { + "appName": "TestApp", + "appId": "test123", + "sessionId": "session123", + "commitHash": "commit123", + "platform": "Windows", + "pythonVersion": "3.9", + "terminalVersion": "1.2.3", + "branch": "main", + "userId": "user123", + }, + ), + ], +) +def test_extract_log_extra(handler, record, expected_extra): + # Act + result = handler.extract_log_extra(record) + + # Assert + assert result == expected_extra diff --git a/openbb_sdk/sdk/core/tests/app/logs/test_handlers_manager.py b/openbb_sdk/sdk/core/tests/app/logs/test_handlers_manager.py new file mode 100644 index 00000000000..f6abf90be8c --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/logs/test_handlers_manager.py @@ -0,0 +1,74 @@ +import logging +from unittest.mock import Mock, patch + +from openbb_core.app.logs.handlers_manager import HandlersManager + + +class MockPosthogHandler(logging.NullHandler): + def __init__(self, settings): + self.settings = settings + + +class MockPathTrackingFileHandler(logging.NullHandler): + def __init__(self, settings): + self.settings = settings + + +class MockFormatterWithExceptions(logging.Formatter): + def __init__(self, settings): + self.settings = settings + + +def test_handlers_added_correctly(): + with patch( + "openbb_core.app.logs.handlers_manager.PosthogHandler", + MockPosthogHandler, + ), patch( + "openbb_core.app.logs.handlers_manager.PathTrackingFileHandler", + MockPathTrackingFileHandler, + ), patch( + "openbb_core.app.logs.handlers_manager.FormatterWithExceptions", + MockFormatterWithExceptions, + ): + settings = Mock() + settings.handler_list = ["stdout", "stderr", "noop", "file", "posthog"] + _ = HandlersManager(settings=settings) + + handlers = logging.getLogger().handlers + + assert len(handlers) >= 5 + + for handler in handlers: + assert isinstance(handler, (logging.NullHandler, logging.StreamHandler)) + + for mock in [MockPosthogHandler, MockPathTrackingFileHandler]: + assert any(isinstance(handler, mock) for handler in handlers) + + +def test_update_handlers(): + with patch( + "openbb_core.app.logs.handlers_manager.PosthogHandler", + MockPosthogHandler, + ), patch( + "openbb_core.app.logs.handlers_manager.PathTrackingFileHandler", + MockPathTrackingFileHandler, + ), patch( + "openbb_core.app.logs.handlers_manager.FormatterWithExceptions", + MockFormatterWithExceptions, + ): + settings = Mock() + settings.handler_list = ["file", "posthog"] + settings.any_other_attr = "mock_settings" + handlers_manager = HandlersManager(settings=settings) + + changed_settings = Mock() + changed_settings.any_other_attr = "changed_settings" + + handlers_manager.update_handlers(settings=changed_settings) + + handlers = logging.getLogger().handlers + + for hdlr in handlers: + if isinstance(hdlr, (MockPosthogHandler, MockPathTrackingFileHandler)): + assert hdlr.settings == changed_settings + assert hdlr.formatter.settings == changed_settings diff --git a/openbb_sdk/sdk/core/tests/app/logs/test_logging_manager.py b/openbb_sdk/sdk/core/tests/app/logs/test_logging_manager.py new file mode 100644 index 00000000000..b409b3489aa --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/logs/test_logging_manager.py @@ -0,0 +1,188 @@ +import json +from typing import Optional +from unittest.mock import MagicMock, Mock, patch + +import pytest +from openbb_core.app.logs.logging_manager import LoggingManager +from pydantic import BaseModel + + +class MockLoggingSettings: + def __init__(self, system_settings, user_settings): + self.system_settings = system_settings + self.user_settings = user_settings + + +class MockCommandOutput(BaseModel): + output: Optional[str] + error: Optional[str] + + +@pytest.fixture(scope="function") +def logging_manager(): + mock_system_settings = Mock() + mock_system_settings = "mock_system_settings" + mock_user_settings = Mock() + mock_user_settings = "mock_user_settings" + mock_setup_handlers = Mock() + mock_log_startup = Mock() + + with patch( + "openbb_core.app.logs.logging_manager.LoggingSettings", + MockLoggingSettings, + ), patch( + "openbb_core.app.logs.logging_manager.LoggingManager._setup_handlers", + mock_setup_handlers, + ), patch( + "openbb_core.app.logs.logging_manager.LoggingManager._log_startup", + mock_log_startup, + ): + logging_manager = LoggingManager( + system_settings=mock_system_settings, + user_settings=mock_user_settings, + ) + + assert mock_setup_handlers.assert_called_once + assert mock_log_startup.assert_called_once + + return logging_manager + + +def test_correctly_initialized(logging_manager): + assert logging_manager + + +def test_logging_settings_property(logging_manager): + assert logging_manager.logging_settings.system_settings == "mock_system_settings" + assert logging_manager.logging_settings.user_settings == "mock_user_settings" + + +def test_logging_settings_setter(logging_manager): + custom_user_settings = "custom_user_settings" + custom_system_settings = "custom_system_settings" + + with patch( + "openbb_core.app.logs.logging_manager.LoggingSettings", + MockLoggingSettings, + ): + logging_manager.logging_settings = ( + custom_system_settings, + custom_user_settings, + ) + + assert logging_manager.logging_settings.system_settings == "custom_system_settings" + assert logging_manager.logging_settings.user_settings == "custom_user_settings" + + +def test_log_startup(logging_manager): + with patch("logging.getLogger") as mock_get_logger: + mock_info = mock_get_logger.return_value.info + + class MockCredentials(BaseModel): + username: str + password: str + + logging_manager._user_settings = MagicMock( + preferences="your_preferences", + credentials=MockCredentials(username="username", password="password"), + ) + logging_manager._system_settings = "your_system_settings" + + logging_manager._log_startup() + + expected_log_data = { + "PREFERENCES": "your_preferences", + "KEYS": {"username": "defined", "password": "defined"}, + "SYSTEM": "your_system_settings", + } + mock_info.assert_called_once_with( + "STARTUP: %s ", + json.dumps(expected_log_data), + ) + mock_get_logger.assert_called_once + + +@pytest.mark.parametrize( + "user_settings, system_settings, command_output, route, func, kwargs", + [ + ( + "mock_settings", + "mock_system", + MockCommandOutput(output="mock_output"), + "mock_route", + "mock_func", + {}, + ), + ( + "mock_settings", + "mock_system", + MockCommandOutput(error="mock_error"), + "mock_route", + "mock_func", + {}, + ), + ( + "mock_settings", + "mock_system", + MockCommandOutput(error="mock_error"), + "login", + "mock_func", + {}, + ), + ], +) +def test_log( + logging_manager, user_settings, system_settings, command_output, route, func, kwargs +): + with patch( + "openbb_core.app.logs.logging_manager.LoggingSettings", + MockLoggingSettings, + ), patch("logging.getLogger") as mock_get_logger: + if route == "login": + with patch( + "openbb_core.app.logs.logging_manager.LoggingManager._log_startup" + ) as mock_log_startup: + logging_manager.log( + user_settings=user_settings, + system_settings=system_settings, + command_output=command_output, + route=route, + func=func, + kwargs=kwargs, + ) + assert mock_log_startup.assert_called_once + + else: + mock_info = mock_get_logger.return_value.info + mock_error = mock_get_logger.return_value.error + + mock_callable = Mock() + mock_callable.__name__ = func + + logging_manager.log( + user_settings=user_settings, + system_settings=system_settings, + command_output=command_output, + route=route, + func=mock_callable, + kwargs=kwargs, + ) + + expected_log_data = { + "route": route, + "input": kwargs, + "error": command_output.error, + } + + if command_output.error: + mock_error.assert_called_once_with( + "ERROR: %s", + json.dumps(expected_log_data), + extra={"func_name_override": func}, + ) + else: + mock_info.assert_called_once_with( + "CMD: %s", + json.dumps(expected_log_data), + extra={"func_name_override": func}, + ) diff --git a/openbb_sdk/sdk/core/tests/app/logs/utils/test_expired_files.py b/openbb_sdk/sdk/core/tests/app/logs/utils/test_expired_files.py new file mode 100644 index 00000000000..4be38a6892d --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/logs/utils/test_expired_files.py @@ -0,0 +1,148 @@ +import os +import tempfile +from pathlib import Path +from time import time +from unittest.mock import MagicMock + +import pytest +from openbb_core.app.logs.utils.expired_files import ( + get_expired_file_list, + get_timestamp_from_x_days, + remove_file_list, +) + + +@pytest.fixture +def temp_test_files(): + with tempfile.TemporaryDirectory() as temp_dir: + temp_files = [ + Path(temp_dir) / "file1.txt", + Path(temp_dir) / "file2.txt", + Path(temp_dir) / "file3.txt", + ] + + # Create some test files + for file_path in temp_files: + file_path.touch() + + yield temp_dir, temp_files + + +def test_get_timestamp_from_x_days(): + result = get_timestamp_from_x_days(0) + assert isinstance(result, float) + + +# Test case when all files are expired +def test_all_files_expired(temp_test_files): + temp_dir, temp_files = temp_test_files + before_timestamp = time() + 3 * 86400 # timestamp 3 days from now + expired_files = get_expired_file_list(Path(temp_dir), before_timestamp) + assert set(expired_files) == set(temp_files) + + +# Test case when no files are expired +def test_no_files_expired(temp_test_files): + temp_dir, _ = temp_test_files + before_timestamp = 0 + expired_files = get_expired_file_list(Path(temp_dir), before_timestamp) + assert expired_files == [] + + +# Test case when some files are expired and some are not +def test_some_files_expired(temp_test_files): + temp_dir, _ = temp_test_files + + # add temp file to temp_dir with timestamp in the future + temp_file = Path(temp_dir) / "file4.txt" + temp_file.touch() + time_in_future = time() + 4 * 86400 # timestamp 4 days from now + os.utime(temp_file, times=(time_in_future, time_in_future)) + + before_timestamp = time() + 3 * 86400 # timestamp 3 days from now + expired_files = get_expired_file_list(Path(temp_dir), before_timestamp) + assert len(expired_files) == 3 + + # assert number of files in temp_dir is 4 + assert len(list(Path(temp_dir).iterdir())) == 4 + + +# Test case when the directory does not exist +def test_directory_not_exists(): + directory = Path("/path/that/does/not/exist") + before_timestamp = time() + expired_files = get_expired_file_list(directory, before_timestamp) + assert expired_files == [] + + +# Test case when the directory is not a directory +def test_directory_not_dir(temp_test_files): + _, temp_files = temp_test_files + before_timestamp = time() + expired_files = get_expired_file_list(temp_files[0], before_timestamp) + assert expired_files == [] + + +@pytest.fixture +def mock_path(): + # Create a MagicMock for the Path class to represent a file path + return MagicMock() + + +def test_remove_file_list_no_files(mock_path, mocker): + # Arrange + # Let's assume the file list is empty, meaning there are no files to remove + file_list = [] + + # Act + remove_file_list(file_list) + + # Assert + # No interaction with the filesystem should occur + assert not mock_path.unlink.called + + +def test_remove_file_list_remove_files_successfully(mock_path, mocker): + # Arrange + # Let's assume we have three files in the file list + file_list = [mock_path, mock_path, mock_path] + + # Mock the unlink method to avoid actual filesystem interaction + mocker.patch.object(mock_path, "unlink") + + # Act + remove_file_list(file_list) + + # Assert + # unlink should have been called three times since there are three files in the list + assert mock_path.unlink.call_count == 3 + + +def test_remove_file_list_ignore_permission_error(mock_path, mocker): + # Arrange + # Let's assume we have three files in the file list + file_list = [mock_path, mock_path, mock_path] + + # Mock the unlink method to raise a PermissionError + mocker.patch.object(mock_path, "unlink", side_effect=PermissionError) + + # Act + remove_file_list(file_list) + + # Assert + # unlink should have been called three times since there are three files in the list + assert mock_path.unlink.call_count == 3 + + +def test_remove_file_list_other_exception(mock_path, mocker): + # Arrange + # Let's assume we have three files in the file list + file_list = [mock_path, mock_path, mock_path] + + # Mock the unlink method to raise an exception other than PermissionError + mocker.patch.object(mock_path, "unlink", side_effect=OSError) + + # Act & Assert + # The exception should be raised since it is not suppressed by the contextlib + with pytest.raises(OSError): + remove_file_list(file_list) diff --git a/openbb_sdk/sdk/core/tests/app/logs/utils/test_system_utils.py b/openbb_sdk/sdk/core/tests/app/logs/utils/test_system_utils.py new file mode 100644 index 00000000000..1995a8adbe0 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/logs/utils/test_system_utils.py @@ -0,0 +1,9 @@ +# TODO : add tests once the code is fixed + + +def test_get_commit_hash(): + pass + + +def test_get_branch(): + pass diff --git a/openbb_sdk/sdk/core/tests/app/logs/utils/test_utils.py b/openbb_sdk/sdk/core/tests/app/logs/utils/test_utils.py new file mode 100644 index 00000000000..f3715abc434 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/logs/utils/test_utils.py @@ -0,0 +1,93 @@ +import uuid +from pathlib import Path +from unittest.mock import patch + +import pytest +from openbb_core.app.logs.utils.utils import get_app_id, get_log_dir, get_session_id + +## get_session_id + + +def test_get_session_id_return_type(): + # Test if the returned value is a string + session_id = get_session_id() + assert isinstance(session_id, str) + + +def test_get_session_id_format(): + # Test if the returned string has the format "UUID-current_time" + session_id = get_session_id() + + parts = session_id.split("-") + + assert len(parts) == 6 + + uuid_part = "-".join(parts[0:5]) + time_part = str(parts[5]) + + # Check if the first part (UUID) is a valid UUID + assert uuid.UUID(uuid_part) + + # Check if the second part (current_time) is numeric + assert int(time_part) + + +def test_get_session_id_uniqueness(): + # Test if subsequent calls return different session IDs + session_id1 = get_session_id() + session_id2 = get_session_id() + assert session_id1 != session_id2 + + +## get_app_id + + +def test_get_app_id_success(): + # Mock the return value of get_log_dir to simulate a successful scenario + with patch("openbb_core.app.logs.utils.utils.get_log_dir") as mock_get_log_dir: + mock_get_log_dir + mock_get_log_dir.return_value = Path( + "/path/to/contextual_user_data_directory/app_id.log" + ) + app_id = get_app_id("/path/to/contextual_user_data_directory") + assert app_id == "app_id" + + +def test_get_app_id_os_error(): + # Test handling of OSError with errno 30 (Read-only file system) + with patch("openbb_core.app.logs.utils.utils.get_log_dir") as mock_get_log_dir: + mock_get_log_dir.side_effect = OSError(30, "Read-only file system") + with pytest.raises(OSError): + get_app_id("/path/to/contextual_user_data_directory") + + +def test_get_app_id_other_exception(): + # Test handling of other exceptions + with patch("openbb_core.app.logs.utils.utils.get_log_dir") as mock_get_log_dir: + mock_get_log_dir.side_effect = Exception("Some other error") + with pytest.raises(Exception, match="Some other error"): + get_app_id("/path/to/contextual_user_data_directory") + + +## get_log_dir + + +def test_get_log_dir(): + with patch( + "openbb_core.app.logs.utils.utils.create_log_dir_if_not_exists", + return_value="/test_dir", + ) as mock_create_log_dir, patch( + "openbb_core.app.logs.utils.utils.create_log_uuid_if_not_exists", + return_value="12345", + ) as mock_create_log_uuid, patch( + "openbb_core.app.logs.utils.utils.create_uuid_dir_if_not_exists", + return_value="/test_dir/12345", + ) as mock_create_uuid_dir: + # Call the get_log_dir function + result = get_log_dir("contextual_user_data_directory") + + # Assertions + assert result == "/test_dir/12345" + mock_create_log_dir.assert_called_once_with("contextual_user_data_directory") + mock_create_log_uuid.assert_called_once_with("/test_dir") + mock_create_uuid_dir.assert_called_once_with("/test_dir", "12345") diff --git a/openbb_sdk/sdk/core/tests/app/model/abstract/test_error.py b/openbb_sdk/sdk/core/tests/app/model/abstract/test_error.py new file mode 100644 index 00000000000..4e16d152580 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/abstract/test_error.py @@ -0,0 +1,24 @@ +import pytest +from openbb_core.app.model.abstract.error import Error + + +@pytest.mark.parametrize( + "error_kind, message", + [ + ("test", "test"), + ("test2", "test2"), + ], +) +def test_error_model(error_kind, message): + err = Error(error_kind=error_kind, message=message) + + assert err.error_kind == error_kind + assert err.message == message + + +def test_fields(): + fields = Error.__fields__ + fields_keys = fields.keys() + + assert "error_kind" in fields_keys + assert "message" in fields_keys diff --git a/openbb_sdk/sdk/core/tests/app/model/abstract/test_results.py b/openbb_sdk/sdk/core/tests/app/model/abstract/test_results.py new file mode 100644 index 00000000000..da1fc0776b6 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/abstract/test_results.py @@ -0,0 +1,8 @@ +from openbb_core.app.model.abstract.results import Results +from pydantic import BaseModel + + +def test_results_model(): + res = Results() + + assert isinstance(res, BaseModel) diff --git a/openbb_sdk/sdk/core/tests/app/model/abstract/test_singleton.py b/openbb_sdk/sdk/core/tests/app/model/abstract/test_singleton.py new file mode 100644 index 00000000000..d87502e2f67 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/abstract/test_singleton.py @@ -0,0 +1,32 @@ +from openbb_core.app.model.abstract.singleton import SingletonMeta + + +class MyClass(metaclass=SingletonMeta): + def __init__(self, value): + self.value = value + + +def test_singleton_instance_creation(): + # Arrange + instance1 = MyClass(42) + instance2 = MyClass(100) + + # Act & Assert + assert instance1 is instance2 + assert instance1.value == instance2.value + assert instance1.value == 42 + + +def test_singleton_multiple_classes(): + # Arrange + class AnotherClass(metaclass=SingletonMeta): + def __init__(self, data): + self.data = data + + instance1 = MyClass(42) + instance2 = AnotherClass("test") + + # Act & Assert + assert instance1 is not instance2 + assert instance1.value == 42 + assert instance2.data == "test" diff --git a/openbb_sdk/sdk/core/tests/app/model/abstract/test_tagged.py b/openbb_sdk/sdk/core/tests/app/model/abstract/test_tagged.py new file mode 100644 index 00000000000..9a8ecd6c12e --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/abstract/test_tagged.py @@ -0,0 +1,14 @@ +from openbb_core.app.model.abstract.tagged import Tagged + + +def test_tagged_model(): + tagged = Tagged() + + assert hasattr(tagged, "id") + + +def test_fields(): + fields = Tagged.__fields__ + fields_keys = fields.keys() + + assert "id" in fields_keys diff --git a/openbb_sdk/sdk/core/tests/app/model/abstract/test_warning.py b/openbb_sdk/sdk/core/tests/app/model/abstract/test_warning.py new file mode 100644 index 00000000000..5c466442f12 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/abstract/test_warning.py @@ -0,0 +1,36 @@ +from unittest.mock import Mock + +import pytest +from openbb_core.app.model.abstract.warning import Warning_, cast_warning + + +@pytest.mark.parametrize( + "category, message", + [ + ("test", "test"), + ("test2", "test2"), + ], +) +def test_warn_model(category, message): + war = Warning_(category=category, message=message) + + assert war.category == category + assert war.message == message + + +def test_fields(): + fields = Warning_.__fields__ + fields_keys = fields.keys() + + assert "category" in fields_keys + assert "message" in fields_keys + + +def test_cast_warning(): + mock_warning_message = Mock() + mock_warning_message.category.__name__ = "test" + mock_warning_message.message = "test" + warning = cast_warning(mock_warning_message) + + assert warning.category == "test" + assert warning.message == "test" diff --git a/openbb_sdk/sdk/core/tests/app/model/charts/test_chart.py b/openbb_sdk/sdk/core/tests/app/model/charts/test_chart.py new file mode 100644 index 00000000000..176f5815701 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/charts/test_chart.py @@ -0,0 +1,55 @@ +import pytest +from openbb_core.app.model.charts.chart import Chart, ChartFormat, Error + + +def test_chart_default_values(): + # Arrange & Act + chart = Chart() + + # Assert + assert chart.content is None + assert chart.format == ChartFormat.plotly + assert chart.error is None + + +def test_chart_custom_values(): + # Arrange + content = {"data": [1, 2, 3]} + chart_format = ChartFormat.plotly + error = Error(message="Test Error") + + # Act + chart = Chart(content=content, format=chart_format, error=error) + + # Assert + assert chart.content == content + assert chart.format == chart_format + assert chart.error == error + + +def test_chart_assignment_validation(): + # Arrange + chart = Chart() + + # Act & Assert + with pytest.raises(ValueError): + chart.invalid_field = "Invalid Value" + + +def test_chart_config_validation(): + # Arrange + content = {"data": [1, 2, 3]} + chart_format = ChartFormat.plotly + + chart = Chart(content=content, format=chart_format) + + with pytest.raises(ValueError): + chart.content = "Invalid Content" + + assert chart.content == content + assert chart.format == chart_format + + +def test_show(): + # TODO : add test after the function is properly refactored + pass diff --git a/openbb_sdk/sdk/core/tests/app/model/charts/test_chart_settings.py b/openbb_sdk/sdk/core/tests/app/model/charts/test_chart_settings.py new file mode 100644 index 00000000000..d1174804186 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/charts/test_chart_settings.py @@ -0,0 +1,49 @@ +from unittest.mock import MagicMock + +from openbb_core.app.model.charts.charting_settings import ChartingSettings + +mock_user_settings = MagicMock() +mock_user_settings.preferences.data_directory = "mock_data_directory" +mock_user_settings.preferences.plot_enable_pywry = True +mock_user_settings.preferences.plot_pywry_width = 100 +mock_user_settings.preferences.plot_pywry_height = 100 +mock_user_settings.preferences.plot_open_export = True +mock_user_settings.profile.hub_session.email = "mock_email" +mock_user_settings.profile.hub_session.user_uuid = "mock_uuid" +mock_user_settings.preferences.export_directory = "mock_export_directory" +mock_user_settings.preferences.user_styles_directory = "mock_styles_directory" +mock_user_settings.preferences.chart_style = "mock_chart_style" + +mock_system_settings = MagicMock() +mock_system_settings.log_collect = True +mock_system_settings.version = "mock_version" +mock_system_settings.python_version = "mock_python_version" +mock_system_settings.test_mode = True +mock_system_settings.debug_mode = True +mock_system_settings.headless = True + + +def test_charting_settings(mocker): + mocker.patch( + target="openbb_core.app.model.charts.charting_settings.get_app_id", + new=mocker.Mock(return_value="mock_app_id"), + ) + charting_settings = ChartingSettings( + user_settings=mock_user_settings, system_settings=mock_system_settings + ) + assert charting_settings.log_collect is True + assert charting_settings.version == "mock_version" + assert charting_settings.python_version == "mock_python_version" + assert charting_settings.test_mode is True + assert charting_settings.app_id == "mock_app_id" + assert charting_settings.debug_mode is True + assert charting_settings.headless is True + assert charting_settings.plot_enable_pywry is True + assert charting_settings.plot_pywry_width == 100 + assert charting_settings.plot_pywry_height == 100 + assert charting_settings.plot_open_export is True + assert charting_settings.user_email == "mock_email" + assert charting_settings.user_uuid == "mock_uuid" + assert charting_settings.user_exports_directory == "mock_export_directory" + assert charting_settings.user_styles_directory == "mock_styles_directory" + assert charting_settings.chart_style == "mock_chart_style" diff --git a/openbb_sdk/sdk/core/tests/app/model/hub/test_features_keys.py b/openbb_sdk/sdk/core/tests/app/model/hub/test_features_keys.py new file mode 100644 index 00000000000..19eda989243 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/hub/test_features_keys.py @@ -0,0 +1,8 @@ +from openbb_core.app.model.hub.features_keys import FeaturesKeys + + +def test_features_keys(): + fk = FeaturesKeys(API_BITQUERY_KEY="test", API_BIZTOC_TOKEN="test2") + + assert fk.API_BITQUERY_KEY == "test" + assert fk.API_BIZTOC_TOKEN == "test2" diff --git a/openbb_sdk/sdk/core/tests/app/model/hub/test_hub_session.py b/openbb_sdk/sdk/core/tests/app/model/hub/test_hub_session.py new file mode 100644 index 00000000000..1f55189599c --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/hub/test_hub_session.py @@ -0,0 +1,30 @@ +from openbb_core.app.model.hub.hub_session import HubSession + + +def test_hub_session(): + session = HubSession( + access_token="mock_access_token", + token_type="mock_token_type", + email="mock_email", + user_uuid="mock_user_uuid", + username="mock_username", + primary_usage="mock_primary_usage", + ) + assert session.access_token == "mock_access_token" + assert session.token_type == "mock_token_type" + assert session.email == "mock_email" + assert session.user_uuid == "mock_user_uuid" + assert session.username == "mock_username" + assert session.primary_usage == "mock_primary_usage" + + +def test_fields(): + fields = HubSession.__fields__ + fields_keys = fields.keys() + + assert "access_token" in fields_keys + assert "token_type" in fields_keys + assert "email" in fields_keys + assert "user_uuid" in fields_keys + assert "username" in fields_keys + assert "primary_usage" in fields_keys diff --git a/openbb_sdk/sdk/core/tests/app/model/hub/test_hub_user_settings.py b/openbb_sdk/sdk/core/tests/app/model/hub/test_hub_user_settings.py new file mode 100644 index 00000000000..ca2f78ff9a8 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/hub/test_hub_user_settings.py @@ -0,0 +1,7 @@ +from openbb_core.app.model.hub.hub_user_settings import HubUserSettings + + +def test_hub_user_settings(): + hub_settings = HubUserSettings() + + assert isinstance(hub_settings, HubUserSettings) diff --git a/openbb_sdk/sdk/core/tests/app/model/test_command_context.py b/openbb_sdk/sdk/core/tests/app/model/test_command_context.py new file mode 100644 index 00000000000..38a346b2387 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/test_command_context.py @@ -0,0 +1,20 @@ +from openbb_core.app.model.command_context import ( + CommandContext, + SystemSettings, + UserSettings, +) + + +def test_command_context(): + cc = CommandContext() + assert isinstance(cc, CommandContext) + assert isinstance(cc.user_settings, UserSettings) + assert isinstance(cc.system_settings, SystemSettings) + + +def test_fields(): + fields = CommandContext.__fields__ + fields_keys = fields.keys() + + assert "user_settings" in fields_keys + assert "system_settings" in fields_keys diff --git a/openbb_sdk/sdk/core/tests/app/model/test_command_output.py b/openbb_sdk/sdk/core/tests/app/model/test_command_output.py new file mode 100644 index 00000000000..82a9ea57322 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/test_command_output.py @@ -0,0 +1,182 @@ +from unittest.mock import MagicMock, patch + +import pandas as pd +import pytest +from openbb_core.app.model.command_output import Chart, CommandOutput, OpenBBError + + +def test_command_output(): + co = CommandOutput() + assert isinstance(co, CommandOutput) + + +def test_fields(): + fields = CommandOutput.__fields__.keys() + + assert "results" in fields + assert "provider" in fields + assert "warnings" in fields + assert "error" in fields + assert "chart" in fields + + +def test_to_dataframe_no_results(): + co = CommandOutput() + with pytest.raises(Exception): + co.to_dataframe() + + +@pytest.mark.parametrize( + "results, expected_df", + [ + # Test case 1: Normal results with "date" column + ( + [{"date": "2023-07-30", "value": 10}, {"date": "2023-07-31", "value": 20}], + pd.DataFrame( + {"value": [10, 20]}, + index=pd.to_datetime(["2023-07-30", "2023-07-31"]), + ), + ), + # Test case 2: Normal results without "date" column + ( + [{"value": 10}, {"value": 20}], + pd.DataFrame({"value": [10, 20]}, index=pd.RangeIndex(start=0, stop=2)), + ), + # Test case 3: Empty results + ([], pd.DataFrame()), + # Test case 4: Results as None, should raise OpenBBError + (None, OpenBBError("Results not found.")), + ], +) +def test_to_dataframe(results, expected_df): + # Arrange + co = CommandOutput(results=results) + + # Act and Assert + if isinstance(expected_df, pd.DataFrame): + result = co.to_dataframe() + assert result.equals(expected_df) + else: + with pytest.raises(expected_df.__class__) as exc_info: + co.to_dataframe() + + assert str(exc_info.value) == str(expected_df) + + +@pytest.mark.parametrize( + "results, expected", + [ + # Test case 1: Normal results with "date" column + ( + [{"date": "2023-07-30", "value": 10}, {"date": "2023-07-31", "value": 20}], + { + "date": [pd.to_datetime("2023-07-30"), pd.to_datetime("2023-07-31")], + "value": [10, 20], + }, + ), + # Test case 2: Normal results without "date" column + ( + [{"value": 10}, {"value": 20}], + { + "value": [10, 20], + }, + ), + # Test case 3: Empty results + ([], {}), + ], +) +def test_to_dict(results, expected): + # Arrange + co = CommandOutput(results=results) + + # Act + result = co.to_dict() + result.pop("index", None) + + # Assert + assert result == expected + + +@patch("openbb_core.app.model.command_output.ChartingManager") +@patch("openbb_core.app.model.command_output.Chart") +def test_to_plotly_json_with_existing_chart(mock_chart, mock_charting_manager): + # Arrange + mock_instance = CommandOutput() + mock_instance.chart = Chart(content={"existing_chart_data": "some_data"}) + + # Act + result = mock_instance.to_plotly_json() + + # Assert + assert result == {"existing_chart_data": "some_data"} + + # Ensure ChartingManager and Chart constructors were not called + mock_charting_manager.assert_not_called() + mock_chart.assert_not_called() + + +@patch("openbb_core.app.model.command_output.CommandOutput.to_dataframe") +@patch("openbb_core.app.model.command_output.ChartingManager") +@patch("openbb_core.app.model.command_output.Chart") +def test_to_plotly_json_with_new_chart( + mock_chart, + mock_charting_manager, + mock_to_dataframe, +): + def get_mock_dataframe(): + data = { + "col1": [1, 2, 3], + "col2": ["a", "b", "c"], + "col3": [True, False, True], + } + + return pd.DataFrame(data) + + # Arrange + mock_instance = CommandOutput() + mock_charting_manager_instance = mock_charting_manager.return_value + mock_charting_manager_instance.to_plotly_json.return_value = { + "new_chart_data": "some_data" + } + mock_to_dataframe.return_value = get_mock_dataframe() + + # Act + result = mock_instance.to_plotly_json( + create_chart=True, chart_option="option_value" + ) + + # Assert + assert result == {"new_chart_data": "some_data"} + + # Ensure self.to_dataframe() was called + mock_to_dataframe.assert_called_once() + + # Ensure ChartingManager was called with the correct parameters + mock_charting_manager_instance.to_plotly_json.assert_called_once() + + # Ensure Chart constructor was called with the correct parameters + mock_chart.assert_called_once_with( + content={"new_chart_data": "some_data"}, format="plotly" + ) + + +def test_show_chart_exists(): + mock_instance = CommandOutput() + # Arrange + mock_instance.chart = MagicMock(spec=Chart) + + # Act + mock_instance.show() + + # Assert + mock_instance.chart.show.assert_called_once() + + +def test_show_chart_not_found(): + mock_instance = CommandOutput() + # Arrange + mock_instance.chart = None + + # Act and Assert + with pytest.raises(OpenBBError, match="Chart not found."): + mock_instance.show() diff --git a/openbb_sdk/sdk/core/tests/app/model/test_credentials.py b/openbb_sdk/sdk/core/tests/app/model/test_credentials.py new file mode 100644 index 00000000000..099142989d7 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/test_credentials.py @@ -0,0 +1,22 @@ +import typing +from unittest.mock import patch + + +def test_credentials(): + with patch( + target="openbb_core.app.model.credentials.get_provider_interface" + ) as mock_provider_interface: + mock_provider_interface.required_credentials = { + "benzinga_api_key": (typing.Optional[str], None), + "polygon_api_key": (typing.Optional[str], None), + } + from openbb_core.app.model.credentials import Credentials + + creds = Credentials( + benzinga_api_key="mock_benzinga_api_key", + polygon_api_key="mock_polygon_api_key", + ) + + assert isinstance(creds, Credentials) + assert creds.benzinga_api_key == "mock_benzinga_api_key" + assert creds.polygon_api_key == "mock_polygon_api_key" diff --git a/openbb_sdk/sdk/core/tests/app/model/test_defaults.py b/openbb_sdk/sdk/core/tests/app/model/test_defaults.py new file mode 100644 index 00000000000..e134eb36214 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/test_defaults.py @@ -0,0 +1,14 @@ +from openbb_core.app.model.defaults import Defaults + + +def test_defaultst(): + cc = Defaults(routes={"test": {"test": "test"}}) + assert isinstance(cc, Defaults) + assert cc.routes == {"test": {"test": "test"}} + + +def test_fields(): + fields = Defaults.__fields__ + fields_keys = fields.keys() + + assert "routes" in fields_keys diff --git a/openbb_sdk/sdk/core/tests/app/model/test_journal.py b/openbb_sdk/sdk/core/tests/app/model/test_journal.py new file mode 100644 index 00000000000..2a90a627616 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/test_journal.py @@ -0,0 +1,19 @@ +from openbb_core.app.model.journal import Journal + + +def test_journal_query(): + journal_query = Journal( + name="mock_name", + journal_entry_id_list=["id_1", "id_2"], + ) + assert isinstance(journal_query, Journal) + assert journal_query.name == "mock_name" + assert journal_query.journal_entry_id_list == ["id_1", "id_2"] + + +def test_fields(): + fields = Journal.__fields__ + fields_keys = fields.keys() + + assert "name" in fields_keys + assert "journal_entry_id_list" in fields_keys diff --git a/openbb_sdk/sdk/core/tests/app/model/test_journal_entry.py b/openbb_sdk/sdk/core/tests/app/model/test_journal_entry.py new file mode 100644 index 00000000000..b1fc6ecd234 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/test_journal_entry.py @@ -0,0 +1,30 @@ +import datetime + +from openbb_core.app.model.journal_entry import JournalEntry + + +def test_journal_entry(): + journal = JournalEntry( + journal_id="mock_journal_id", + arguments={"mock_key": "mock_value"}, + duration=19, + output={}, + route="mock_route", + timestamp=datetime.datetime(2021, 1, 1), + alias_list=["mock_alias"], + ) + assert isinstance(journal, JournalEntry) + + +def test_fields(): + fields = JournalEntry.__fields__ + fields_keys = fields.keys() + + assert "id" in fields_keys + assert "journal_id" in fields_keys + assert "arguments" in fields_keys + assert "duration" in fields_keys + assert "output" in fields_keys + assert "route" in fields_keys + assert "timestamp" in fields_keys + assert "alias_list" in fields_keys diff --git a/openbb_sdk/sdk/core/tests/app/model/test_journal_query.py b/openbb_sdk/sdk/core/tests/app/model/test_journal_query.py new file mode 100644 index 00000000000..0db1432beb0 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/test_journal_query.py @@ -0,0 +1,19 @@ +from openbb_core.app.model.journal_query import JournalQuery + + +def test_journal_query(): + journal_query = JournalQuery( + journal_entry_id="mock_journal_entry_id", + ignore_error_output=True, + ) + assert isinstance(journal_query, JournalQuery) + assert journal_query.journal_entry_id == "mock_journal_entry_id" + assert journal_query.ignore_error_output is True + + +def test_fields(): + fields = JournalQuery.__fields__ + fields_keys = fields.keys() + + assert "journal_entry_id" in fields_keys + assert "ignore_error_output" in fields_keys diff --git a/openbb_sdk/sdk/core/tests/app/model/test_preferences.py b/openbb_sdk/sdk/core/tests/app/model/test_preferences.py new file mode 100644 index 00000000000..ca3bae14af0 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/test_preferences.py @@ -0,0 +1,6 @@ +from openbb_core.app.model.preferences import Preferences + + +def test_preferences(): + preferences = Preferences() + assert isinstance(preferences, Preferences) diff --git a/openbb_sdk/sdk/core/tests/app/model/test_profile.py b/openbb_sdk/sdk/core/tests/app/model/test_profile.py new file mode 100644 index 00000000000..5c22d7ee73d --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/test_profile.py @@ -0,0 +1,6 @@ +from openbb_core.app.model.profile import Profile + + +def test_preferences(): + preferences = Profile() + assert isinstance(preferences, Profile) diff --git a/openbb_sdk/sdk/core/tests/app/model/test_system_settings.py b/openbb_sdk/sdk/core/tests/app/model/test_system_settings.py new file mode 100644 index 00000000000..a34b124a24c --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/test_system_settings.py @@ -0,0 +1,203 @@ +import os +from unittest.mock import patch + +import pytest +from openbb_core.app.model.system_settings import SystemSettings + + +def test_system_settings(): + sys = SystemSettings() + assert isinstance(sys, SystemSettings) + + +def test_create_openbb_directory_directory_and_files_not_exist(tmpdir): + # Arrange + values = { + "openbb_directory": str(tmpdir.join("openbb")), + "user_settings_path": str(tmpdir.join("user_settings.json")), + "system_settings_path": str(tmpdir.join("system_settings.json")), + } + + # Act + SystemSettings.create_openbb_directory(values) + + # Assert + assert os.path.exists(values["openbb_directory"]) + assert os.path.exists(values["user_settings_path"]) + assert os.path.exists(values["system_settings_path"]) + + +def test_create_openbb_directory_directory_exists_user_settings_missing(tmpdir): + # Arrange + values = { + "openbb_directory": str(tmpdir.join("openbb")), + "user_settings_path": str(tmpdir.join("user_settings.json")), + "system_settings_path": str(tmpdir.join("system_settings.json")), + } + + # Create the openbb directory + os.makedirs(values["openbb_directory"]) + + # Act + SystemSettings.create_openbb_directory(values) + + # Assert + assert os.path.exists(values["openbb_directory"]) + assert os.path.exists(values["user_settings_path"]) + assert os.path.exists(values["system_settings_path"]) + + +def test_create_openbb_directory_directory_exists_system_settings_missing(tmpdir): + # Arrange + values = { + "openbb_directory": str(tmpdir.join("openbb")), + "user_settings_path": str(tmpdir.join("user_settings.json")), + "system_settings_path": str(tmpdir.join("system_settings.json")), + } + + # Create the openbb directory + os.makedirs(values["openbb_directory"]) + + # Create the user_settings.json file + with open(values["user_settings_path"], "w") as f: + f.write("{}") + + # Act + SystemSettings.create_openbb_directory(values) + + # Assert + assert os.path.exists(values["openbb_directory"]) + assert os.path.exists(values["user_settings_path"]) + assert os.path.exists(values["system_settings_path"]) + + +@pytest.mark.parametrize( + "values, expected_handlers", + [ + # Test case: test_mode is True, logging_suppress is True + ( + { + "test_mode": True, + "logging_suppress": True, + "log_collect": True, + "logging_handlers": [], + }, + [], + ), + # Test case: test_mode is False, logging_suppress is True + ( + { + "test_mode": False, + "logging_suppress": True, + "log_collect": True, + "logging_handlers": [], + }, + [], + ), + # Test case: test_mode is False, logging_suppress is False, log_collect is True, + # and "posthog" handler is not present in logging_handlers + ( + { + "test_mode": False, + "logging_suppress": False, + "log_collect": True, + "logging_handlers": ["file", "console"], + }, + ["file", "console", "posthog"], + ), + # Test case: test_mode is False, logging_suppress is False, log_collect is True, + # and "posthog" handler is already present in logging_handlers + ( + { + "test_mode": False, + "logging_suppress": False, + "log_collect": True, + "logging_handlers": ["file", "console", "posthog"], + }, + ["file", "console", "posthog"], + ), + ], +) +def test_validate_posthog_handler(values, expected_handlers): + # Act + result = SystemSettings.validate_posthog_handler(values) + + # Assert + assert result["logging_handlers"] == expected_handlers + + +@pytest.mark.parametrize( + "handlers, valid", + [ + # Test case: Valid handlers + (["stdout", "file", "noop"], True), + # Test case: Invalid handler + (["stdout", "invalid_handler", "file"], False), + # Test case: Empty list of handlers + ([], True), + # Test case: Repeated valid handlers + (["stdout", "stderr", "stdout", "noop", "stderr"], True), + ], +) +def test_validate_logging_handlers(handlers, valid): + # Act and Assert + if valid: + assert SystemSettings.validate_logging_handlers(handlers) == handlers + else: + with pytest.raises(ValueError, match="Invalid logging handler"): + SystemSettings.validate_logging_handlers(handlers) + + +@pytest.mark.parametrize( + "commit_hash, expected_result", + [ + # Test case: Valid commit hash provided + ("1234567890", "1234567890"), + # Test case: Empty commit hash, mocked get_commit_hash returns "abcdef1234" + ("", "abcdef1234"), + ], +) +def test_validate_commit_hash(commit_hash, expected_result): + with patch( + "openbb_core.app.model.system_settings.get_commit_hash", + return_value="abcdef1234", + ): + # Act + result = SystemSettings.validate_commit_hash(commit_hash) + + # Assert + assert result == expected_result + + +@patch("openbb_core.app.model.system_settings.get_branch", return_value="main") +@pytest.mark.parametrize( + "branch, commit_hash, expected_branch", + [ + # Test case: Empty branch, non-empty commit hash + ("", "abcdef1234", "main"), + # Test case: Non-empty branch, non-empty commit hash + ("feature-branch", "abcdef1234", "feature-branch"), + # Test case: Empty branch, empty commit hash + ("", "", ""), + # Test case: Non-empty branch, empty commit hash + ("feature-branch", "", "feature-branch"), + ], +) +def test_validate_branch(mock_get_branch, branch, commit_hash, expected_branch): + # Arrange + values = { + "logging_branch": branch, + "logging_commit_hash": commit_hash, + } + + # Act + result = SystemSettings.validate_branch(values) + + # Assert + assert result["logging_branch"] == expected_branch + + # Ensure that get_branch was called with the correct commit_hash + if not branch and commit_hash: + mock_get_branch.assert_called_once_with(commit_hash) + else: + mock_get_branch.assert_not_called() diff --git a/openbb_sdk/sdk/core/tests/app/model/test_user_settings.py b/openbb_sdk/sdk/core/tests/app/model/test_user_settings.py new file mode 100644 index 00000000000..85fe58e1375 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/model/test_user_settings.py @@ -0,0 +1,13 @@ +from unittest.mock import MagicMock + +from openbb_core.app.model.user_settings import UserSettings + + +def test_user_settings(): + settings = UserSettings( + credentials=MagicMock(), + profile=MagicMock(), + preferences=MagicMock(), + defaults=MagicMock(), + ) + assert isinstance(settings, UserSettings) diff --git a/openbb_sdk/sdk/core/tests/app/repository/abstract/test_access_token_repository.py b/openbb_sdk/sdk/core/tests/app/repository/abstract/test_access_token_repository.py new file mode 100644 index 00000000000..626063a5224 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/repository/abstract/test_access_token_repository.py @@ -0,0 +1,56 @@ +from typing import Any, List, Optional, Tuple, TypeVar + +from openbb_core.app.repository.abstract.access_token_repository import ( + AccessTokenRepository, +) +from pydantic import BaseModel + +T = TypeVar("T", bound=BaseModel) + + +def test_journal_entry_repository(): + class TestAccessTokenRepository(AccessTokenRepository): + def __init__(self, test_arg_1, test_arg_2): + self.test_arg_1 = test_arg_1 + self.test_arg_2 = test_arg_2 + super().__init__() + + @property + def collection_name(self): + return "test_collection" + + def create(self, model): + pass + + @property + def database_name(self): + return "test_database" + + def delete(self, filter_list: Optional[List[Tuple[str, Any]]] = None): + pass + + def delete_all(self, filter_list: Optional[List[Tuple[str, Any]]] = None): + pass + + def read( + self, + field_list: Optional[List[str]] = None, + filter_list: Optional[List[Tuple[str, Any]]] = None, + ): + pass + + def read_all( + self, + field_list: Optional[List[str]] = None, + filter_list: Optional[List[Tuple[str, Any]]] = None, + ): + pass + + def update(self, model: T): + pass + + def mock_method(self): + return self.test_arg_1, self.test_arg_2 + + jr = TestAccessTokenRepository("xpto_1", "xpto_2") + assert isinstance(jr, AccessTokenRepository) diff --git a/openbb_sdk/sdk/core/tests/app/repository/abstract/test_journal_entry_repository.py b/openbb_sdk/sdk/core/tests/app/repository/abstract/test_journal_entry_repository.py new file mode 100644 index 00000000000..23c3d2240b3 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/repository/abstract/test_journal_entry_repository.py @@ -0,0 +1,56 @@ +from typing import Any, List, Optional, Tuple, TypeVar + +from openbb_core.app.repository.abstract.journal_entry_repository import ( + JournalEntryRepository, +) +from pydantic import BaseModel + +T = TypeVar("T", bound=BaseModel) + + +def test_journal_entry_repository(): + class TestJournalEntryRepository(JournalEntryRepository): + def __init__(self, test_arg_1, test_arg_2): + self.test_arg_1 = test_arg_1 + self.test_arg_2 = test_arg_2 + super().__init__() + + @property + def collection_name(self): + return "test_collection" + + def create(self, model): + pass + + @property + def database_name(self): + return "test_database" + + def delete(self, filter_list: Optional[List[Tuple[str, Any]]] = None): + pass + + def delete_all(self, filter_list: Optional[List[Tuple[str, Any]]] = None): + pass + + def read( + self, + field_list: Optional[List[str]] = None, + filter_list: Optional[List[Tuple[str, Any]]] = None, + ): + pass + + def read_all( + self, + field_list: Optional[List[str]] = None, + filter_list: Optional[List[Tuple[str, Any]]] = None, + ): + pass + + def update(self, model: T): + pass + + def mock_method(self): + return self.test_arg_1, self.test_arg_2 + + jr = TestJournalEntryRepository("xpto_1", "xpto_2") + assert isinstance(jr, JournalEntryRepository) diff --git a/openbb_sdk/sdk/core/tests/app/repository/abstract/test_journal_repository.py b/openbb_sdk/sdk/core/tests/app/repository/abstract/test_journal_repository.py new file mode 100644 index 00000000000..072abd9f57d --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/repository/abstract/test_journal_repository.py @@ -0,0 +1,56 @@ +from typing import Any, List, Optional, Tuple, TypeVar + +from openbb_core.app.repository.abstract.journal_repository import ( + JournalRepository, +) +from pydantic import BaseModel + +T = TypeVar("T", bound=BaseModel) + + +def test_journal_repository(): + class TestJournalRepository(JournalRepository): + def __init__(self, test_arg_1, test_arg_2): + self.test_arg_1 = test_arg_1 + self.test_arg_2 = test_arg_2 + super().__init__() + + @property + def collection_name(self): + return "test_collection" + + def create(self, model): + pass + + @property + def database_name(self): + return "test_database" + + def delete(self, filter_list: Optional[List[Tuple[str, Any]]] = None): + pass + + def delete_all(self, filter_list: Optional[List[Tuple[str, Any]]] = None): + pass + + def read( + self, + field_list: Optional[List[str]] = None, + filter_list: Optional[List[Tuple[str, Any]]] = None, + ): + pass + + def read_all( + self, + field_list: Optional[List[str]] = None, + filter_list: Optional[List[Tuple[str, Any]]] = None, + ): + pass + + def update(self, model: T): + pass + + def mock_method(self): + return self.test_arg_1, self.test_arg_2 + + jr = TestJournalRepository("xpto_1", "xpto_2") + assert isinstance(jr, JournalRepository) diff --git a/openbb_sdk/sdk/core/tests/app/repository/abstract/test_repository.py b/openbb_sdk/sdk/core/tests/app/repository/abstract/test_repository.py new file mode 100644 index 00000000000..3ebf1de7d6d --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/repository/abstract/test_repository.py @@ -0,0 +1,56 @@ +from typing import Any, List, Optional, Tuple, TypeVar + +from openbb_core.app.repository.abstract.repository import ( + Repository, +) +from pydantic import BaseModel + +T = TypeVar("T", bound=BaseModel) + + +def test_repository(): + class TestRepository(Repository): + def __init__(self, test_arg_1, test_arg_2): + self.test_arg_1 = test_arg_1 + self.test_arg_2 = test_arg_2 + super().__init__() + + @property + def collection_name(self): + return "test_collection" + + def create(self, model): + pass + + @property + def database_name(self): + return "test_database" + + def delete(self, filter_list: Optional[List[Tuple[str, Any]]] = None): + pass + + def delete_all(self, filter_list: Optional[List[Tuple[str, Any]]] = None): + pass + + def read( + self, + field_list: Optional[List[str]] = None, + filter_list: Optional[List[Tuple[str, Any]]] = None, + ): + pass + + def read_all( + self, + field_list: Optional[List[str]] = None, + filter_list: Optional[List[Tuple[str, Any]]] = None, + ): + pass + + def update(self, model: T): + pass + + def mock_method(self): + return self.test_arg_1, self.test_arg_2 + + jr = TestRepository("xpto_1", "xpto_2") + assert isinstance(jr, Repository) diff --git a/openbb_sdk/sdk/core/tests/app/repository/abstract/test_user_settings_repository.py b/openbb_sdk/sdk/core/tests/app/repository/abstract/test_user_settings_repository.py new file mode 100644 index 00000000000..e310a275b83 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/repository/abstract/test_user_settings_repository.py @@ -0,0 +1,63 @@ +from typing import Any, List, Optional, Tuple, TypeVar + +from openbb_core.app.repository.abstract.user_settings_repository import ( + UserSettingsRepository, +) +from pydantic import BaseModel + +T = TypeVar("T", bound=BaseModel) + + +def test_journal_entry_repository(): + class TestUserSettingsRepository(UserSettingsRepository): + def __init__(self, test_arg_1, test_arg_2): + self.test_arg_1 = test_arg_1 + self.test_arg_2 = test_arg_2 + super().__init__() + + @property + def collection_name(self): + return "test_collection" + + def create(self, model): + pass + + @property + def database_name(self): + return "test_database" + + def delete(self, filter_list: Optional[List[Tuple[str, Any]]] = None): + pass + + def delete_all(self, filter_list: Optional[List[Tuple[str, Any]]] = None): + pass + + def read( + self, + field_list: Optional[List[str]] = None, + filter_list: Optional[List[Tuple[str, Any]]] = None, + ): + pass + + def read_all( + self, + field_list: Optional[List[str]] = None, + filter_list: Optional[List[Tuple[str, Any]]] = None, + ): + pass + + def update(self, model: T): + pass + + def mock_method(self): + return self.test_arg_1, self.test_arg_2 + + def read_by_profile( + self, + field_list: Optional[List[str]] = None, + filter_list: Optional[List[Tuple[str, Any]]] = None, + ): + pass + + jr = TestUserSettingsRepository("xpto_1", "xpto_2") + assert isinstance(jr, UserSettingsRepository) diff --git a/openbb_sdk/sdk/core/tests/app/repository/base/test_in_memory_repository.py b/openbb_sdk/sdk/core/tests/app/repository/base/test_in_memory_repository.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/repository/base/test_mongodb_repository.py b/openbb_sdk/sdk/core/tests/app/repository/base/test_mongodb_repository.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/repository/in_memory/test_access_token_repository.py b/openbb_sdk/sdk/core/tests/app/repository/in_memory/test_access_token_repository.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/repository/in_memory/test_journal_entry_repository.py b/openbb_sdk/sdk/core/tests/app/repository/in_memory/test_journal_entry_repository.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/repository/in_memory/test_journal_repository.py b/openbb_sdk/sdk/core/tests/app/repository/in_memory/test_journal_repository.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/repository/in_memory/test_user_settings_repository.py b/openbb_sdk/sdk/core/tests/app/repository/in_memory/test_user_settings_repository.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/repository/mongodb/test_access_token_repository.py b/openbb_sdk/sdk/core/tests/app/repository/mongodb/test_access_token_repository.py new file mode 100644 index 00000000000..2300f71b6f6 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/repository/mongodb/test_access_token_repository.py @@ -0,0 +1,19 @@ +from unittest.mock import patch + +from openbb_core.app.repository.mongodb.access_token_repository import ( + AccessTokenRepository, +) + + +@patch("openbb_core.app.repository.mongodb.access_token_repository.MongoClient") +def test_access_token_repository(mock_mongo_client): + atr = AccessTokenRepository( + client=mock_mongo_client, + collection_name="collection_name", + database_name="database_name", + ) + + assert isinstance(atr, AccessTokenRepository) + assert atr.client == mock_mongo_client + assert atr.collection_name == "collection_name" + assert atr.database_name == "database_name" diff --git a/openbb_sdk/sdk/core/tests/app/repository/mongodb/test_journal_entry_repository.py b/openbb_sdk/sdk/core/tests/app/repository/mongodb/test_journal_entry_repository.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/repository/mongodb/test_journal_repository.py b/openbb_sdk/sdk/core/tests/app/repository/mongodb/test_journal_repository.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/repository/mongodb/test_user_settings_repository.py b/openbb_sdk/sdk/core/tests/app/repository/mongodb/test_user_settings_repository.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/core/tests/app/results/test_empty.py b/openbb_sdk/sdk/core/tests/app/results/test_empty.py new file mode 100644 index 00000000000..23a73ff369e --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/results/test_empty.py @@ -0,0 +1,9 @@ +from openbb_core.app.model.results.empty import Empty +from pydantic import BaseModel + + +def test_empty_model(): + empty = Empty() + + assert isinstance(empty, Empty) + assert isinstance(empty, BaseModel) diff --git a/openbb_sdk/sdk/core/tests/app/service/test_hub_service.py b/openbb_sdk/sdk/core/tests/app/service/test_hub_service.py new file mode 100644 index 00000000000..ff3ff68ed1e --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/service/test_hub_service.py @@ -0,0 +1,174 @@ +"""Test the hub_service.py module.""" + + +from unittest.mock import MagicMock, patch + +import pytest +from openbb_core.app.service.hub_service import ( + Credentials, + HubService, + HubSession, + HubUserSettings, + OpenBBError, +) + + +@pytest.fixture +def mocker(): + """Fixture for mocker.""" + with patch("openbb_core.app.service.hub_service.HubService") as mock: + yield mock + + +def test_connect_with_email_password(): + """Test connect with email and password.""" + mock_hub_session = MagicMock(spec=HubSession) + with patch( + "requests.post", return_value=MagicMock(status_code=200, json=lambda: {}) + ): + with patch.object( + HubService, "get_session_from_email_password", return_value=mock_hub_session + ): + hub_service = HubService() + result = hub_service.connect(email="test@example.com", password="password") + + assert result == mock_hub_session + assert hub_service.session == mock_hub_session + + +def test_connect_with_sdk_token(): + """Test connect with SDK personal access token.""" + mock_hub_session = MagicMock(spec=HubSession) + with patch( + "requests.post", return_value=MagicMock(status_code=200, json=lambda: {}) + ): + with patch.object( + HubService, "get_session_from_sdk_token", return_value=mock_hub_session + ): + hub_service = HubService() + result = hub_service.connect(sdk_token="sdk_token") + + assert result == mock_hub_session + assert hub_service.session == mock_hub_session + + +def test_connect_without_credentials(): + """Test connect without credentials.""" + hub_service = HubService() + with pytest.raises( + OpenBBError, match="Please provide 'email' and 'password' or 'sdk_token'" + ): + hub_service.connect() + + +@patch("requests.post") +def test_get_session_from_email_password(mock_post): + """Test get session from email and password.""" + mock_post.return_value = MagicMock( + status_code=200, + json=lambda: { + "access_token": "token", + "token_type": "Bearer", + "uuid": "uuid", + "email": "email", + "username": "username", + "primary_usage": "primary_usage", + }, + ) + result = HubService.get_session_from_email_password("email", "password") + assert isinstance(result, HubSession) + + +@patch("requests.post") +def test_get_session_from_sdk_token(mock_post): + """Test get session from SDK personal access token.""" + mock_post.return_value = MagicMock( + status_code=200, + json=lambda: { + "access_token": "token", + "token_type": "Bearer", + "uuid": "uuid", + "username": "username", + "email": "email", + "primary_usage": "primary_usage", + }, + ) + + mock_token = ( + "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImRiMjEyZDdhZj" + "c2MWI0ZTNlOGNjZGM3OWQ5Zjk4YWM5In0.eyJhY2Nlc3NfdG9rZW4iOiJ0" + "b2tlbiIsInRva2VuX3R5cGUiOiJCZWFyZXIiLCJ1dWlkIjoidXVpZCIsInV" + "zZXJuYW1lIjoidXNlcm5hbWUiLCJlbWFpbCI6ImVtYWlsIiwicHJpbWFyeV9" + "1c2FnZSI6InByaW1hcnlfdXNhZ2UifQ.FAtE8-a1a-313Zoa6dREIxGZOHaW9" + "-JLZnFzyJ6dlHBZnkjQT2tfaaefxnTdAlSmToQwxGykvuatmI7L0wztPQ" + ) + + result = HubService.get_session_from_sdk_token(mock_token) + assert isinstance(result, HubSession) + + +@patch("requests.get") +def test_disconnect(mock_get): + """Test disconnect.""" + mock_get.return_value = MagicMock(status_code=200, json=lambda: {"success": True}) + mock_hub_session = MagicMock( + spec=HubSession, access_token="token", token_type="Bearer" + ) + hub_service = HubService(session=mock_hub_session) + + assert hub_service.disconnect() is True + assert hub_service.session is None + + +@patch("requests.get") +def test_get_user_settings(mock_get): + """Test get user settings.""" + mock_get.return_value = MagicMock(status_code=200, json=lambda: {}) + mock_hub_session = MagicMock( + spec=HubSession, access_token="token", token_type="Bearer" + ) + + user_settings = HubService.get_user_settings(mock_hub_session) + assert isinstance(user_settings, HubUserSettings) + + +@patch("requests.put") +def test_put_user_settings(mock_put): + """Test put user settings.""" + mock_put.return_value = MagicMock(status_code=200) + mock_hub_session = MagicMock( + spec=HubSession, access_token="token", token_type="Bearer" + ) + mock_user_settings = MagicMock(spec=HubUserSettings) + + assert HubService.put_user_settings(mock_hub_session, mock_user_settings) is True + + +def test_hub2sdk(): + """Test hub2sdk.""" + mock_user_settings = MagicMock(spec=HubUserSettings) + mock_user_settings.features_keys = MagicMock( + API_KEY_FINANCIALMODELINGPREP="fmp", + API_POLYGON_KEY="polygon", + API_FRED_KEY="fred", + ) + + credentials = HubService.hub2sdk(mock_user_settings) + assert isinstance(credentials, Credentials) + assert credentials.fmp_api_key == "fmp" + assert credentials.polygon_api_key == "polygon" + assert credentials.fred_api_key == "fred" + + +def test_sdk2hub(): + """Test sdk2hub.""" + mock_credentials = MagicMock(spec=Credentials) + mock_credentials.fmp_api_key = "fmp" + mock_credentials.polygon_api_key = "polygon" + mock_credentials.fred_api_key = "fred" + + user_settings = HubService.sdk2hub(mock_credentials) + assert isinstance(user_settings, HubUserSettings) + assert user_settings.features_keys.API_KEY_FINANCIALMODELINGPREP == "fmp" + assert user_settings.features_keys.API_POLYGON_KEY == "polygon" + assert user_settings.features_keys.API_FRED_KEY == "fred" diff --git a/openbb_sdk/sdk/core/tests/app/service/test_journal_service.py b/openbb_sdk/sdk/core/tests/app/service/test_journal_service.py new file mode 100644 index 00000000000..2e735c43d17 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/service/test_journal_service.py @@ -0,0 +1,103 @@ +"""Test the journal_service.py module.""" +# pylint: disable=redefined-outer-name + +from unittest.mock import MagicMock + +import pytest +from openbb_core.app.service.journal_service import ( + AbstractJournalEntryRepository, + InMemoryJournalEntryRepository, + JournalService, + MongoClient, + MongoDBJournalEntryRepository, +) + + +@pytest.fixture +def journal_service(): + """Fixture for journal service.""" + return JournalService() + + +def test_journal_service_init(journal_service): + """Test journal service init.""" + assert journal_service + + +def test_invalid_client(journal_service): + """Test valid client.""" + assert not journal_service.valid_client() + + +def test_build_journal_entry_repository_with_given_repository(): + """Test build with repository.""" + mock_repository = MagicMock(spec=AbstractJournalEntryRepository) + result = JournalService.build_journal_entry_repository( + valid_client=True, journal_entry_repository=mock_repository + ) + + assert result == mock_repository + + +def test_build_journal_entry_repository_with_mongodb_client(): + """Test build with mongodb client.""" + mock_client = MagicMock(spec=MongoClient) + result = JournalService.build_journal_entry_repository( + valid_client=True, mongodb_client=mock_client + ) + + assert isinstance(result, MongoDBJournalEntryRepository) + + +def test_build_journal_entry_repository_memory(): + """Test build with in-memory access.""" + result = JournalService.build_journal_entry_repository(valid_client=True) + + assert isinstance(result, InMemoryJournalEntryRepository) + + +def test_build_journal_repository_with_given_repository(): + """Test build with repository.""" + mock_repository = MagicMock(spec=AbstractJournalEntryRepository) + result = JournalService.build_journal_entry_repository( + valid_client=True, journal_entry_repository=mock_repository + ) + + assert result == mock_repository + + +def test_build_journal_repository_with_mongodb_client(): + """Test build with mongodb client.""" + mock_client = MagicMock(spec=MongoClient) + result = JournalService.build_journal_entry_repository( + valid_client=True, mongodb_client=mock_client + ) + + assert isinstance(result, MongoDBJournalEntryRepository) + + +def test_build_journal_repository_memory(): + """Test build with in-memory access.""" + result = JournalService.build_journal_entry_repository(valid_client=True) + + assert isinstance(result, InMemoryJournalEntryRepository) + + +def test_journal_service_mongodb_client(journal_service): + """Test mongodb client.""" + assert journal_service.mongodb_client is None + + +def test_journal_service_journal_entry_repository(journal_service): + """Test journal entry repository.""" + assert isinstance( + journal_service.journal_entry_repository, InMemoryJournalEntryRepository + ) + + +def test_journal_service_journal_repository(journal_service): + """Test journal repository.""" + assert journal_service.journal_repository + assert isinstance( + type(journal_service.journal_repository), type(InMemoryJournalEntryRepository) + ) diff --git a/openbb_sdk/sdk/core/tests/app/service/test_system_service.py b/openbb_sdk/sdk/core/tests/app/service/test_system_service.py new file mode 100644 index 00000000000..6b42c18c581 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/service/test_system_service.py @@ -0,0 +1,54 @@ +"""Test the system_service.py module.""" +# pylint: disable=redefined-outer-name + +import pytest +from openbb_core.app.service.system_service import SystemService + + +@pytest.fixture +def system_service(): + """Fixture for system service.""" + return SystemService() + + +def test_system_service_init(system_service): + """Test system service init.""" + assert system_service + + +def test_read_default_system_settings(system_service): + """Test read default system settings.""" + system_settings = system_service.read_default_system_settings() + + assert system_settings + + +def test_write_default_system_settings(system_service): + """Test write default system settings.""" + system_settings = system_service.read_default_system_settings() + system_service.write_default_system_settings(system_settings=system_settings) + + assert system_service + + +def test_system_settings(system_service): + """Test system settings.""" + system_settings = system_service.system_settings + + assert system_settings + + +def test_system_settings_setter(system_service): + """Test system settings setter.""" + system_settings = system_service.system_settings + + system_service.system_settings = system_settings + + assert system_service.system_settings == system_settings + + +def test_refresh_system_settings(system_service): + """Test refresh system settings.""" + system_settings = system_service.refresh_system_settings() + + assert system_settings diff --git a/openbb_sdk/sdk/core/tests/app/service/test_user_service.py b/openbb_sdk/sdk/core/tests/app/service/test_user_service.py new file mode 100644 index 00000000000..11b3ee4ba7f --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/service/test_user_service.py @@ -0,0 +1,119 @@ +"""Test the user_service.py module.""" + +from pathlib import Path +from unittest.mock import MagicMock + +import pytest +from openbb_core.app.service.user_service import ( + AbstractAccessTokenRepository, + InMemoryAccessTokenRepository, + MongoClient, + MongoDBAccessTokenRepository, + UserService, + UserSettings, +) + + +def test_build_token_repository_with_given_repository(): + """Test build with repository.""" + mock_repository = MagicMock(spec=AbstractAccessTokenRepository) + result = UserService.build_token_repository( + valid_client=True, access_token_repository=mock_repository + ) + + assert result == mock_repository + + +def test_build_token_repository_with_mongodb_client(): + """Test build with mongodb client.""" + mock_client = MagicMock(spec=MongoClient) + result = UserService.build_token_repository( + valid_client=True, mongodb_client=mock_client + ) + + assert isinstance(result, MongoDBAccessTokenRepository) + + +def test_build_token_repository_memory(): + """Test build with in-memory access.""" + result = UserService.build_token_repository(valid_client=True) + + assert isinstance(result, InMemoryAccessTokenRepository) + + +def test_build_user_settings_repository_with_given_repository(): + """Test build with repository.""" + mock_repository = MagicMock(spec=AbstractAccessTokenRepository) + result = UserService.build_user_settings_repository( + valid_client=True, user_settings_repository=mock_repository + ) + + assert result == mock_repository + + +def test_build_user_settings_repository_with_mongodb_client(): + """Test build with mongodb client.""" + mock_client = MagicMock(spec=MongoClient) + result = UserService.build_user_settings_repository( + valid_client=True, mongodb_client=mock_client + ) + + assert result + assert isinstance(type(result), type(MongoDBAccessTokenRepository)) + + +def test_build_user_settings_repository_memory(): + """Test build with in-memory access.""" + result = UserService.build_user_settings_repository(valid_client=True) + + assert result + assert isinstance(type(result), type(InMemoryAccessTokenRepository)) + + +def test_valid_client_false(): + """Test valid client.""" + result = UserService.valid_client() + + assert result is False + + +def test_valid_client_true(): + """Test valid client.""" + mock_client = MagicMock(spec=MongoClient) + result = UserService.valid_client(client=mock_client) + + assert result is True + + +# @pytest.mark.skip(reason="TODO") +def test_read_default_user_settings_file_exists(): + """Test read default user settings.""" + result = UserService.read_default_user_settings(path=Path("some_path")) + + assert result + assert isinstance(result, UserSettings) + + +@pytest.mark.skip(reason="TODO") +def test_write_default_user_settings(): + """Test write default user settings.""" + + +@pytest.mark.skip(reason="TODO") +def test_update_default(): + """Test update default user settings.""" + + +def test_merge_dicts(): + """Test merge dicts.""" + result = UserService.merge_dicts( + list_of_dicts=[ + {"a": 1, "b": 2}, + {"a": 3, "b": 4}, + ] + ) + + assert result + assert isinstance(result, dict) + assert result["a"] == 3 + assert result["b"] == 4 diff --git a/openbb_sdk/sdk/core/tests/app/static/test_app_factory.py b/openbb_sdk/sdk/core/tests/app/static/test_app_factory.py new file mode 100644 index 00000000000..863ca944117 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/static/test_app_factory.py @@ -0,0 +1,48 @@ +"""Test static app factory.""" +# pylint: disable=redefined-outer-name + +import pytest +from openbb_core.app.model.system_settings import SystemSettings +from openbb_core.app.model.user_settings import UserSettings +from openbb_core.app.static.account import Account +from openbb_core.app.static.app_factory import create_app +from openbb_core.app.static.coverage import Coverage + + +@pytest.fixture(scope="module") +def app_factory(): + """Return app factory.""" + return create_app() + + +def test_app_factory_init(app_factory): + """Test app factory init.""" + assert app_factory + + +def test_app_system_settings(app_factory): + """Test app system settings.""" + system_settings = app_factory.system + assert system_settings + assert isinstance(system_settings, SystemSettings) + + +def test_app_user_settings(app_factory): + """Test app user settings.""" + user_settings = app_factory.settings + assert user_settings + assert isinstance(user_settings, UserSettings) + + +def test_app_account(app_factory): + """Test app account.""" + account = app_factory.account + assert account + assert isinstance(account, Account) + + +def test_app_coverage(app_factory): + """Test app coverage.""" + coverage = app_factory.coverage + assert coverage + assert isinstance(coverage, Coverage) diff --git a/openbb_sdk/sdk/core/tests/app/static/test_container.py b/openbb_sdk/sdk/core/tests/app/static/test_container.py new file mode 100644 index 00000000000..dc1e01b2801 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/static/test_container.py @@ -0,0 +1,9 @@ +"""Test the container.py file.""" + +from openbb_core.app.static.container import Container + + +def test_container_init(): + """Test container init.""" + container = Container(None) + assert container diff --git a/openbb_sdk/sdk/core/tests/app/static/test_coverage.py b/openbb_sdk/sdk/core/tests/app/static/test_coverage.py new file mode 100644 index 00000000000..2131d24cefb --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/static/test_coverage.py @@ -0,0 +1,30 @@ +"""Test the coverage.py file.""" +# pylint: disable=redefined-outer-name + +import pytest +from openbb_core.app.static.coverage import Coverage + + +@pytest.fixture(scope="module") +def coverage(): + """Return coverage.""" + return Coverage() + + +def test_coverage_init(coverage): + """Test coverage init.""" + assert coverage + + +def test_coverage_providers(coverage): + """Test coverage providers.""" + provider_coverage = coverage.providers + assert provider_coverage + assert isinstance(provider_coverage, dict) + + +def test_coverage_commands(coverage): + """Test coverage commands.""" + command_coverage = coverage.commands + assert command_coverage + assert isinstance(command_coverage, dict) diff --git a/openbb_sdk/sdk/core/tests/app/static/test_filters.py b/openbb_sdk/sdk/core/tests/app/static/test_filters.py new file mode 100644 index 00000000000..5b014933991 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/static/test_filters.py @@ -0,0 +1,44 @@ +"""Test the filters.py file.""" + +import pytest +from openbb_core.app.model.command_output import CommandOutput +from openbb_core.app.static.filters import filter_call, filter_inputs, filter_output +from pandas import DataFrame + + +def test_filter_call_error(): + """Test filter_call.""" + + class Test: + @filter_call + def test(self): + return 1 / 0 + + test = Test() + + with pytest.raises(Exception): + test.test() + + +def test_filter_inputs_not_df(): + """Test filter_inputs.""" + kwargs = {"num": 1} + kwargs = filter_inputs(**kwargs) + + assert kwargs["num"] == 1 + + +def test_filter_inputs_df(): + """Test filter_inputs.""" + df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]}) + kwargs = {"df": df} + kwargs = filter_inputs(**kwargs) + + assert isinstance(kwargs["df"], list) + + +def test_filter_output(): + """Test filter_output.""" + command_output = filter_output(CommandOutput()) + + assert command_output is not None diff --git a/openbb_sdk/sdk/core/tests/app/static/test_package_builder.py b/openbb_sdk/sdk/core/tests/app/static/test_package_builder.py new file mode 100644 index 00000000000..659061194a2 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/static/test_package_builder.py @@ -0,0 +1,449 @@ +"""Test the package_builder.py file.""" +# pylint: disable=redefined-outer-name + +from dataclasses import dataclass +from inspect import _empty + +import pytest +from openbb_core.app.static.package_builder import ( + ClassDefinition, + ImportDefinition, + Linters, + MethodDefinition, + ModuleBuilder, + PackageBuilder, + Parameter, + PathHandler, +) +from pydantic import Field +from typing_extensions import Annotated + + +@pytest.fixture(scope="module") +def package_builder(): + """Return package builder.""" + return PackageBuilder() + + +def test_package_builder_init(package_builder): + """Test package builder init.""" + assert package_builder + + +@pytest.mark.skip("Rebuilds the packages which takes some time.") +def test_package_builder_build(package_builder): + """Test package builder build.""" + package_builder.build() + + +@pytest.mark.skip() +def test_save_module_map(package_builder): + """Test save module map.""" + package_builder.save_module_map() + + +@pytest.mark.skip() +def test_save_modules(package_builder): + """Test save module.""" + package_builder.save_modules() + + +@pytest.mark.skip() +def test_save_package(package_builder): + """Test save package.""" + package_builder.save_package() + + +def test_run_linters(package_builder): + """Test run linters.""" + package_builder.run_linters() + + +@pytest.mark.skip("We avoid writing to the package.") +def test_write_to_package(package_builder): + """Test save to package.""" + package_builder.write_to_package() + + +@pytest.fixture(scope="module") +def module_builder(): + """Return module builder.""" + return ModuleBuilder() + + +def test_module_builder_init(module_builder): + """Test module builder init.""" + assert module_builder + + +@pytest.fixture(scope="module") +def class_definition(): + """Return class definition.""" + return ClassDefinition() + + +def test_class_definition_init(class_definition): + """Test class definition init.""" + assert class_definition + + +def test_build(class_definition): + """Test build.""" + code = class_definition.build("openbb_core.app.static.container.Container") + assert code + + +@pytest.fixture(scope="module") +def method_definition(): + """Return method definition.""" + return MethodDefinition() + + +def test_method_definition_init(method_definition): + """Test method definition init.""" + assert method_definition + + +def test_build_class_loader_method(method_definition): + """Test build class loader method.""" + code = method_definition.build_class_loader_method( + "openbb_core.app.static.container.Container" + ) + assert code + + +def test_get_type(method_definition): + """Test get type.""" + type_ = method_definition.get_type(field=Parameter.empty) + assert type_ + assert isinstance(type_, type) + + +def test_get_type_hint(method_definition): + """Test get type hint.""" + + class TestField: + type = int + + field = TestField() + result = method_definition.get_type(field) + assert result == int + + +def test_field_with_type_attribute_missing_type(method_definition): + """Test field with type attribute missing type.""" + + class TestField: + type = Parameter.empty + + field = TestField() + result = method_definition.get_type(field) + assert result is Parameter.empty + + +def test_get_default(method_definition): + """Test get default.""" + + class TestField: + default = Field(default=42) + + field = TestField() + result = method_definition.get_default(field) + assert result == 42 + + +def test_get_default_none(method_definition): + """Test get default.""" + + class TestField: + default = 42 + + field = TestField() + result = method_definition.get_default(field) + assert result is None + + +def test_get_default_default_value(method_definition): + """Test get default default value.""" + + class TestField: + default = type(Ellipsis)() + + field = TestField() + result = method_definition.get_default(field) + assert result is None + + +def test_get_default_no_default(method_definition): + """Test get default no default.""" + + class TestField: + pass + + field = TestField() + result = method_definition.get_default(field) + assert result == _empty + + +def test_is_annotated_dc(method_definition): + """Test is annotated dc.""" + result = method_definition.is_annotated_dc(annotation=Parameter.empty) + assert not result + + +def test_is_annotated_dc_annotated(method_definition): + """Test is annotated dc annotated.""" + + @dataclass + class TestAnnotatedDataClass: + """Test annotated data class.""" + + value: int + + annotated_dataclass = Annotated[TestAnnotatedDataClass, "test_annotation"] + result = method_definition.is_annotated_dc(annotation=annotated_dataclass) + assert result + + +def test_reorder_params(method_definition): + """Test reorder params.""" + params = { + "provider": Parameter.empty, + "extra_params": Parameter.empty, + "param1": Parameter.empty, + "param2": Parameter.empty, + } + result = method_definition.reorder_params(params=params) + assert result + assert list(result.keys()) == ["param1", "param2", "provider", "extra_params"] + + +def test_build_func_params(method_definition): + """Test build func params.""" + param_map = { + "param1": Parameter("NoneType", kind=Parameter.POSITIONAL_OR_KEYWORD), + "param2": Parameter("int", kind=Parameter.POSITIONAL_OR_KEYWORD), + } + + expected_output = "param1, param2, chart: bool = False" + output = method_definition.build_func_params(param_map) + + assert output == expected_output + + +@pytest.mark.parametrize( + "return_type, expected_output", + [ + (_empty, "None"), + (int, "int"), + ], +) +def test_build_func_returns(method_definition, return_type, expected_output): + """Test build func returns.""" + output = method_definition.build_func_returns(return_type=return_type) + assert output == expected_output + + +def test_build_command_method_signature(method_definition): + """Test build command method signature.""" + parameter_map = { + "param1": Parameter("NoneType", kind=Parameter.POSITIONAL_OR_KEYWORD), + "param2": Parameter("int", kind=Parameter.POSITIONAL_OR_KEYWORD), + } + return_type = int + output = method_definition.build_command_method_signature( + func_name="test_func", parameter_map=parameter_map, return_type=return_type + ) + assert output + + +def test_build_command_method_doc(method_definition): + """Test build command method doc.""" + + def some_func(): + """Do some func doc.""" + + output = method_definition.build_command_method_doc(func=some_func) + assert output + assert isinstance(output, str) + + +def test_build_command_method_implementation(method_definition): + """Test build command method implementation.""" + + def some_func(): + """Do some func doc.""" + return 42 + + output = method_definition.build_command_method_implementation( + path="openbb_core.app.static.container.Container", func=some_func + ) + + assert output + assert isinstance(output, str) + + +def test_build_command_method(method_definition): + """Test build command method.""" + + def some_func(): + """Do some func doc.""" + return 42 + + output = method_definition.build_command_method( + path="openbb_core.app.static.container.Container", + func=some_func, + model_name=None, + ) + + assert output + assert isinstance(output, str) + + +@pytest.fixture(scope="module") +def import_definition(): + """Return import definition.""" + return ImportDefinition() + + +def test_import_definition_init(import_definition): + """Test import definition init.""" + assert import_definition + + +def test_filter_hint_type_list(import_definition): + """Test filter type hint list.""" + output = import_definition.filter_hint_type_list( + hint_type_list=[int, str, float, bool, _empty, _empty, _empty, _empty] + ) + assert output == [] + + +def test_import_definition_get_path_hint_type_list(import_definition): + """Test import definition get path hint type list.""" + hint_type_list = import_definition.get_path_hint_type_list( + path="openbb_core.app.static.container.Container" + ) + assert hint_type_list == [] + + +def test_import_definition_build(import_definition): + """Test import definition build.""" + code = import_definition.build(path="openbb_core.app.static.container.Container") + assert code + + +@pytest.fixture(scope="module") +def path_handler(): + """Return path handler.""" + return PathHandler() + + +def test_path_handler_init(path_handler): + """Test path handler init.""" + assert path_handler + + +@pytest.fixture(scope="module") +def route_map(path_handler): + """Return route map.""" + return path_handler.build_route_map() + + +def test_build_route_map(route_map): + """Test build route map.""" + assert route_map + assert isinstance(route_map, dict) + + +@pytest.fixture(scope="module") +def path_list(path_handler, route_map): + """Return path list.""" + return path_handler.build_path_list(route_map=route_map) + + +def test_build_path_list(path_list): + """Test build path list.""" + assert path_list + assert isinstance(path_list, list) + + +def test_get_route(path_handler, route_map): + """Test get route.""" + route = path_handler.get_route(route_map=route_map, path="/stocks/load") + + assert route + + +def test_get_child_path_list(path_handler, path_list): + """Test get child path list.""" + child_path_list = path_handler.get_child_path_list( + path="/stocks", path_list=path_list + ) + + assert child_path_list + assert isinstance(child_path_list, list) + + +def test_clean_path(path_handler): + """Test clean path.""" + path = "/stocks/load" + result = path_handler.clean_path(path=path) + assert result == "stocks_load" + + +def test_build_module_name(path_handler): + """Test build module name.""" + module_name = path_handler.build_module_name(path="") + assert module_name == "__extensions__" + + module_name = path_handler.build_module_name(path="/stocks/load") + assert module_name == "stocks_load" + + +def test_build_module_class(path_handler): + """Test build module class.""" + module_class = path_handler.build_module_class(path="") + assert module_class == "Extensions" + + module_class = path_handler.build_module_class(path="/stocks/load") + assert module_class == "CLASS_stocks_load" + + +@pytest.fixture(scope="module") +def linters(): + """Return linters.""" + return Linters() + + +def test_linters_init(linters): + """Test linters init.""" + assert linters + + +def test_print_separator(linters): + """Test print separator.""" + linters.print_separator(symbol="AAPL") + + +def test_run(linters): + """Test run.""" + linters.run(linter="ruff") + + +def test_ruff(linters): + """Test ruff.""" + linters.ruff() + + +def test_black(linters): + """Test black.""" + linters.black() + + +def test_mypy(linters): + """Test mypy.""" + linters.mypy() + + +# TODO: Implement unit test for the DocstringGenerator class once refactored. diff --git a/openbb_sdk/sdk/core/tests/app/test_provider_interface.py b/openbb_sdk/sdk/core/tests/app/test_provider_interface.py new file mode 100644 index 00000000000..56483d23571 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/test_provider_interface.py @@ -0,0 +1,85 @@ +"""Test provider interface.""" +# pylint: disable=redefined-outer-name + +import pytest +from openbb_core.app.provider_interface import ( + ProviderChoices, + ProviderInterface, + get_provider_interface, +) + + +@pytest.fixture(scope="module") +def provider_interface(): + """Set up provider_interface.""" + return ProviderInterface() + + +def test_init(provider_interface): + """Test init.""" + assert provider_interface + + +def test_get_provider_interface(): + """Test get provider interface.""" + assert isinstance(get_provider_interface(), ProviderInterface) + + +def test_map(provider_interface): + """Test map.""" + provider_interface_map = provider_interface.map + assert isinstance(provider_interface_map, dict) + assert len(provider_interface_map) > 0 + assert "StockEOD" in provider_interface_map + + +def test_required_credentials(provider_interface): + """Test required credentials.""" + required_credentials = provider_interface.required_credentials + assert isinstance(required_credentials, list) + assert len(required_credentials) > 0 + + +def test_model_providers(provider_interface): + """Test model providers.""" + model_providers = provider_interface.model_providers + assert isinstance(model_providers, dict) + assert len(model_providers) > 0 + + +def test_params(provider_interface): + """Test params.""" + params = provider_interface.params + assert isinstance(params, dict) + assert len(params) > 0 + assert "StockEOD" in params + + +def test_data(provider_interface): + """Test data.""" + data = provider_interface.data + assert isinstance(data, dict) + assert len(data) > 0 + assert "StockEOD" in data + + +def test_providers_literal(provider_interface): + """Test providers literal.""" + providers_literal = provider_interface.providers_literal + assert isinstance(type(providers_literal), type) + assert len(providers_literal.__args__) > 0 + assert "openbb" not in providers_literal.__args__ + + +def test_provider_choices(provider_interface): + """Test provider choices.""" + provider_choices = provider_interface.provider_choices + assert isinstance(provider_choices, type(ProviderChoices)) + + +def test_models(provider_interface): + """Test models.""" + models = provider_interface.models + assert isinstance(models, list) + assert len(models) > 0 + assert "StockEOD" in models diff --git a/openbb_sdk/sdk/core/tests/app/test_query.py b/openbb_sdk/sdk/core/tests/app/test_query.py new file mode 100644 index 00000000000..f5856ff573c --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/test_query.py @@ -0,0 +1,126 @@ +"""Test the Query class.""" +# pylint: disable=redefined-outer-name + +from dataclasses import dataclass +from unittest.mock import MagicMock, patch + +import pytest +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query +from pydantic import BaseModel + + +def create_mock_query(): + """Mock query.""" + + class StockEOD: + """Mock StockEOD class.""" + + start_date = "2020-01-01" + end_date = "2020-01-05" + symbol = "AAPL" + + return StockEOD() + + +def create_mock_extra_params(): + """Mock ExtraParams dataclass.""" + + @dataclass + class StockEOD: + """Mock ExtraParams dataclass.""" + + sort: str = "desc" + + return StockEOD() + + +@pytest.fixture(scope="module") +def query(): + """Set up query.""" + return Query( + cc=CommandContext(), + provider_choices=ProviderChoices(provider="fmp"), + standard_params=StandardParams(), + extra_params=ExtraParams(), + ) + + +def test_init(query): + """Test init.""" + assert query + + +def test_to_query_params(query): + """Test to_query_params.""" + standard_params = create_mock_query() + + assert query.to_query_params(standard_params) + + +def test_filter_extra_params(query): + """Test filter_extra_params.""" + extra_params = create_mock_extra_params() + extra_params = query.filter_extra_params(extra_params, "fmp") + + assert isinstance(extra_params, dict) + assert len(extra_params) == 0 + + +def test_filter_extra_params_wrong_param(query): + """Test filter_extra_params.""" + + @dataclass + class StockEOD: + """Mock ExtraParams dataclass.""" + + sort: str = "desc" + limit: int = 4 + + extra_params = StockEOD() + + assert not query.filter_extra_params(extra_params, "fmp") + + +@pytest.fixture +def mock_registry(): + """Mock registry.""" + with patch( + "openbb_core.app.provider_interface.get_provider_interface" + ) as mock_get_provider_interface: + mock_registry = MagicMock() + mock_get_provider_interface.return_value.build_registry.return_value = ( + mock_registry + ) + yield mock_registry + + +@pytest.fixture +def query_instance(): + """Set up query.""" + standard_params = create_mock_query() + extra_params = create_mock_extra_params() + + cc = CommandContext() + setattr(cc.user_settings.credentials, "fmp_api_key", "1234") + + return Query( + cc=cc, + provider_choices=ProviderChoices(provider="fmp"), + standard_params=standard_params, + extra_params=extra_params, + ) + + +def test_execute_method_fake_credentials(query_instance, mock_registry): + """Test execute method without setting credentials.""" + mock_fetch_result = BaseModel() # Replace with an actual instance of BaseModel + mock_registry.fetch.return_value = mock_fetch_result + + with pytest.raises(Exception): + query_instance.execute() diff --git a/openbb_sdk/sdk/core/tests/app/test_router.py b/openbb_sdk/sdk/core/tests/app/test_router.py new file mode 100644 index 00000000000..d09eac2a633 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/app/test_router.py @@ -0,0 +1,307 @@ +"""Test router.py file.""" +# pylint: disable=redefined-outer-name +# pylint: disable=unused-argument + + +import pytest +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.command_output import CommandOutput +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.router import ( + CommandMap, + CommandValidator, + Router, + RouterLoader, + SignatureInspector, +) +from pydantic import BaseModel + + +@pytest.fixture(scope="module") +def command_validator(): + """Set up command_validator.""" + return CommandValidator() + + +def test_command_validator_init(command_validator): + """Test init.""" + assert command_validator + + +@pytest.mark.parametrize( + "type_, expected", + [ + (str, True), + (int, True), + (float, True), + (bool, True), + (list, True), + (BaseModel, False), + ], +) +def test_is_standard_pydantic_type(command_validator, type_, expected): + """Test is_standard_pydantic_type.""" + # assert command_validator.is_standard_pydantic_type(str) + assert command_validator.is_standard_pydantic_type(type_) == expected + + +def test_is_valid_pydantic_model_type(command_validator): + """Test is_valid_pydantic_model_type.""" + assert command_validator.is_valid_pydantic_model_type(BaseModel) + assert not command_validator.is_valid_pydantic_model_type(str) + + +@pytest.mark.parametrize( + "type_, expected", + [ + (str, True), + (int, True), + (float, True), + (bool, True), + (list, True), + (BaseModel, True), + ], +) +def test_is_serializable_value_type(command_validator, type_, expected): + """Test is_serializable_value_type.""" + assert command_validator.is_serializable_value_type(type_) == expected + + +def test_is_annotated_dc(command_validator): + """Test is_annotated_dc.""" + assert not command_validator.is_annotated_dc(str) + + +def test_check_reserved_param(command_validator): + """Test check_reserved_param.""" + assert command_validator.check_reserved_param("name", str, {}, str, str) is None + assert not command_validator.check_reserved_param("name", str, {}, str, int) + + +def test_check_parameters(command_validator): + """Test check_parameters.""" + + def func(): + pass + + assert command_validator.check_parameters(func) is None + assert not command_validator.check_parameters(BaseModel) + + +def test_check_return_error(command_validator): + """Test check_return fail.""" + with pytest.raises(TypeError): + + def func(): + pass + + command_validator.check_return(func) + + +def test_check_return(command_validator): + """Test check_return.""" + + def valid_function() -> CommandOutput[int]: + return CommandOutput(results=[1, 2, 3]) + + assert command_validator.check_return(valid_function) is None + + +def test_check(command_validator): + """Test check.""" + + def valid_function() -> CommandOutput[int]: + return CommandOutput(results=[1, 2, 3]) + + assert command_validator.check(valid_function) is None + + +@pytest.fixture(scope="module") +def router(): + """Set up router.""" + return Router() + + +def test_router_init(router): + """Test init.""" + assert router + + +def test_command(router): + """Test command.""" + + @router.command + def valid_function() -> CommandOutput[int]: + return CommandOutput(results=[1, 2, 3]) + + assert valid_function + + +def test_include_router(router): + """Test include_router.""" + some_router = Router() + assert router.include_router(some_router) is None + + +@pytest.fixture(scope="module") +def router_loader(): + """Set up router_loader.""" + return RouterLoader() + + +def test_router_loader_init(router_loader): + """Test init.""" + assert router_loader + + +def test_from_extensions(router_loader): + """Test from_extensions.""" + assert router_loader.from_extensions() + + +@pytest.fixture(scope="module") +def signature_inspector(): + """Set up signature_inspector.""" + return SignatureInspector() + + +def test_signature_inspector_init(signature_inspector): + """Test init.""" + assert signature_inspector + + +def test_complete_signature(signature_inspector): + """Test complete_signature.""" + + def sample_function( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, + ) -> CommandOutput: + pass + + model = "StockEOD" + + assert signature_inspector.complete_signature(sample_function, model) + + +def test_complete_signature_error(signature_inspector): + """Test complete_signature.""" + + def valid_function() -> CommandOutput[int]: + return CommandOutput(results=[1, 2, 3]) + + with pytest.raises(AttributeError): + signature_inspector.complete_signature(valid_function, "invalid_model") + + +def test_validate_signature(signature_inspector): + """Test validate_signature.""" + + def sample_function( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, + ) -> CommandOutput: + pass + + expected_signature = { + "cc": CommandContext, + "provider_choices": ProviderChoices, + "standard_params": StandardParams, + "extra_params": ExtraParams, + } + + assert ( + signature_inspector.validate_signature(sample_function, expected_signature) + is None + ) + + +def test_inject_dependency(signature_inspector): + """Test inject_dependency.""" + + def sample_function( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, + ) -> CommandOutput: + pass + + assert signature_inspector.inject_dependency(sample_function, "cc", CommandContext) + + +def test_get_description(signature_inspector): + """Test get_description.""" + + def some_function(): + """Mock function.""" + + assert signature_inspector.get_description(some_function) == some_function.__doc__ + + +def test_get_description_no_doc(signature_inspector): + """Test get_description.""" + + def some_function(): + pass + + assert signature_inspector.get_description(some_function) == "" + + +@pytest.fixture(scope="module") +def command_map(): + """Set up command_map.""" + return CommandMap() + + +def test_command_map_init(command_map): + """Test init.""" + assert command_map + + +def test_map(command_map): + """Test map.""" + assert isinstance(command_map.map, dict) + + +def test_provider_coverage(command_map): + """Test provider_coverage.""" + assert isinstance(command_map.provider_coverage, dict) + + +def test_command_coverage(command_map): + """Test command_coverage.""" + assert isinstance(command_map.command_coverage, dict) + + +def test_get_command_map(command_map, router): + """Test get_command_map.""" + command_map = command_map.get_command_map(router) + assert isinstance(command_map, dict) + assert len(command_map) > 0 + + +def test_get_provider_coverage(command_map, router): + """Test get_provider_coverage.""" + provider_coverage = command_map.get_provider_coverage(router) + assert isinstance(provider_coverage, dict) + + +def test_get_command_coverage(command_map, router): + """Test get_command_coverage.""" + command_coverage = command_map.get_command_coverage(router) + assert isinstance(command_coverage, dict) + + +def test_get_command(command_map): + """Test get_command.""" + command = command_map.get_command("stocks/load") + assert command is None diff --git a/openbb_sdk/sdk/core/tests/test_query.py b/openbb_sdk/sdk/core/tests/test_query.py new file mode 100644 index 00000000000..f5856ff573c --- /dev/null +++ b/openbb_sdk/sdk/core/tests/test_query.py @@ -0,0 +1,126 @@ +"""Test the Query class.""" +# pylint: disable=redefined-outer-name + +from dataclasses import dataclass +from unittest.mock import MagicMock, patch + +import pytest +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query +from pydantic import BaseModel + + +def create_mock_query(): + """Mock query.""" + + class StockEOD: + """Mock StockEOD class.""" + + start_date = "2020-01-01" + end_date = "2020-01-05" + symbol = "AAPL" + + return StockEOD() + + +def create_mock_extra_params(): + """Mock ExtraParams dataclass.""" + + @dataclass + class StockEOD: + """Mock ExtraParams dataclass.""" + + sort: str = "desc" + + return StockEOD() + + +@pytest.fixture(scope="module") +def query(): + """Set up query.""" + return Query( + cc=CommandContext(), + provider_choices=ProviderChoices(provider="fmp"), + standard_params=StandardParams(), + extra_params=ExtraParams(), + ) + + +def test_init(query): + """Test init.""" + assert query + + +def test_to_query_params(query): + """Test to_query_params.""" + standard_params = create_mock_query() + + assert query.to_query_params(standard_params) + + +def test_filter_extra_params(query): + """Test filter_extra_params.""" + extra_params = create_mock_extra_params() + extra_params = query.filter_extra_params(extra_params, "fmp") + + assert isinstance(extra_params, dict) + assert len(extra_params) == 0 + + +def test_filter_extra_params_wrong_param(query): + """Test filter_extra_params.""" + + @dataclass + class StockEOD: + """Mock ExtraParams dataclass.""" + + sort: str = "desc" + limit: int = 4 + + extra_params = StockEOD() + + assert not query.filter_extra_params(extra_params, "fmp") + + +@pytest.fixture +def mock_registry(): + """Mock registry.""" + with patch( + "openbb_core.app.provider_interface.get_provider_interface" + ) as mock_get_provider_interface: + mock_registry = MagicMock() + mock_get_provider_interface.return_value.build_registry.return_value = ( + mock_registry + ) + yield mock_registry + + +@pytest.fixture +def query_instance(): + """Set up query.""" + standard_params = create_mock_query() + extra_params = create_mock_extra_params() + + cc = CommandContext() + setattr(cc.user_settings.credentials, "fmp_api_key", "1234") + + return Query( + cc=cc, + provider_choices=ProviderChoices(provider="fmp"), + standard_params=standard_params, + extra_params=extra_params, + ) + + +def test_execute_method_fake_credentials(query_instance, mock_registry): + """Test execute method without setting credentials.""" + mock_fetch_result = BaseModel() # Replace with an actual instance of BaseModel + mock_registry.fetch.return_value = mock_fetch_result + + with pytest.raises(Exception): + query_instance.execute() diff --git a/openbb_sdk/sdk/core/tests/test_router.py b/openbb_sdk/sdk/core/tests/test_router.py new file mode 100644 index 00000000000..d09eac2a633 --- /dev/null +++ b/openbb_sdk/sdk/core/tests/test_router.py @@ -0,0 +1,307 @@ +"""Test router.py file.""" +# pylint: disable=redefined-outer-name +# pylint: disable=unused-argument + + +import pytest +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.command_output import CommandOutput +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.router import ( + CommandMap, + CommandValidator, + Router, + RouterLoader, + SignatureInspector, +) +from pydantic import BaseModel + + +@pytest.fixture(scope="module") +def command_validator(): + """Set up command_validator.""" + return CommandValidator() + + +def test_command_validator_init(command_validator): + """Test init.""" + assert command_validator + + +@pytest.mark.parametrize( + "type_, expected", + [ + (str, True), + (int, True), + (float, True), + (bool, True), + (list, True), + (BaseModel, False), + ], +) +def test_is_standard_pydantic_type(command_validator, type_, expected): + """Test is_standard_pydantic_type.""" + # assert command_validator.is_standard_pydantic_type(str) + assert command_validator.is_standard_pydantic_type(type_) == expected + + +def test_is_valid_pydantic_model_type(command_validator): + """Test is_valid_pydantic_model_type.""" + assert command_validator.is_valid_pydantic_model_type(BaseModel) + assert not command_validator.is_valid_pydantic_model_type(str) + + +@pytest.mark.parametrize( + "type_, expected", + [ + (str, True), + (int, True), + (float, True), + (bool, True), + (list, True), + (BaseModel, True), + ], +) +def test_is_serializable_value_type(command_validator, type_, expected): + """Test is_serializable_value_type.""" + assert command_validator.is_serializable_value_type(type_) == expected + + +def test_is_annotated_dc(command_validator): + """Test is_annotated_dc.""" + assert not command_validator.is_annotated_dc(str) + + +def test_check_reserved_param(command_validator): + """Test check_reserved_param.""" + assert command_validator.check_reserved_param("name", str, {}, str, str) is None + assert not command_validator.check_reserved_param("name", str, {}, str, int) + + +def test_check_parameters(command_validator): + """Test check_parameters.""" + + def func(): + pass + + assert command_validator.check_parameters(func) is None + assert not command_validator.check_parameters(BaseModel) + + +def test_check_return_error(command_validator): + """Test check_return fail.""" + with pytest.raises(TypeError): + + def func(): + pass + + command_validator.check_return(func) + + +def test_check_return(command_validator): + """Test check_return.""" + + def valid_function() -> CommandOutput[int]: + return CommandOutput(results=[1, 2, 3]) + + assert command_validator.check_return(valid_function) is None + + +def test_check(command_validator): + """Test check.""" + + def valid_function() -> CommandOutput[int]: + return CommandOutput(results=[1, 2, 3]) + + assert command_validator.check(valid_function) is None + + +@pytest.fixture(scope="module") +def router(): + """Set up router.""" + return Router() + + +def test_router_init(router): + """Test init.""" + assert router + + +def test_command(router): + """Test command.""" + + @router.command + def valid_function() -> CommandOutput[int]: + return CommandOutput(results=[1, 2, 3]) + + assert valid_function + + +def test_include_router(router): + """Test include_router.""" + some_router = Router() + assert router.include_router(some_router) is None + + +@pytest.fixture(scope="module") +def router_loader(): + """Set up router_loader.""" + return RouterLoader() + + +def test_router_loader_init(router_loader): + """Test init.""" + assert router_loader + + +def test_from_extensions(router_loader): + """Test from_extensions.""" + assert router_loader.from_extensions() + + +@pytest.fixture(scope="module") +def signature_inspector(): + """Set up signature_inspector.""" + return SignatureInspector() + + +def test_signature_inspector_init(signature_inspector): + """Test init.""" + assert signature_inspector + + +def test_complete_signature(signature_inspector): + """Test complete_signature.""" + + def sample_function( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, + ) -> CommandOutput: + pass + + model = "StockEOD" + + assert signature_inspector.complete_signature(sample_function, model) + + +def test_complete_signature_error(signature_inspector): + """Test complete_signature.""" + + def valid_function() -> CommandOutput[int]: + return CommandOutput(results=[1, 2, 3]) + + with pytest.raises(AttributeError): + signature_inspector.complete_signature(valid_function, "invalid_model") + + +def test_validate_signature(signature_inspector): + """Test validate_signature.""" + + def sample_function( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, + ) -> CommandOutput: + pass + + expected_signature = { + "cc": CommandContext, + "provider_choices": ProviderChoices, + "standard_params": StandardParams, + "extra_params": ExtraParams, + } + + assert ( + signature_inspector.validate_signature(sample_function, expected_signature) + is None + ) + + +def test_inject_dependency(signature_inspector): + """Test inject_dependency.""" + + def sample_function( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, + ) -> CommandOutput: + pass + + assert signature_inspector.inject_dependency(sample_function, "cc", CommandContext) + + +def test_get_description(signature_inspector): + """Test get_description.""" + + def some_function(): + """Mock function.""" + + assert signature_inspector.get_description(some_function) == some_function.__doc__ + + +def test_get_description_no_doc(signature_inspector): + """Test get_description.""" + + def some_function(): + pass + + assert signature_inspector.get_description(some_function) == "" + + +@pytest.fixture(scope="module") +def command_map(): + """Set up command_map.""" + return CommandMap() + + +def test_command_map_init(command_map): + """Test init.""" + assert command_map + + +def test_map(command_map): + """Test map.""" + assert isinstance(command_map.map, dict) + + +def test_provider_coverage(command_map): + """Test provider_coverage.""" + assert isinstance(command_map.provider_coverage, dict) + + +def test_command_coverage(command_map): + """Test command_coverage.""" + assert isinstance(command_map.command_coverage, dict) + + +def test_get_command_map(command_map, router): + """Test get_command_map.""" + command_map = command_map.get_command_map(router) + assert isinstance(command_map, dict) + assert len(command_map) > 0 + + +def test_get_provider_coverage(command_map, router): + """Test get_provider_coverage.""" + provider_coverage = command_map.get_provider_coverage(router) + assert isinstance(provider_coverage, dict) + + +def test_get_command_coverage(command_map, router): + """Test get_command_coverage.""" + command_coverage = command_map.get_command_coverage(router) + assert isinstance(command_coverage, dict) + + +def test_get_command(command_map): + """Test get_command.""" + command = command_map.get_command("stocks/load") + assert command is None diff --git a/openbb_sdk/sdk/provider/README.md b/openbb_sdk/sdk/provider/README.md index 729b4c6fb4e..31271f98346 100644 --- a/openbb_sdk/sdk/provider/README.md +++ b/openbb_sdk/sdk/provider/README.md @@ -28,11 +28,14 @@ Close for closing price column name in data from one provider might be coded as ## Key Elements -- **QueryParams** : Are input parameters that are used for obtaining data. To load stock market data, we would have StockQueryParams, which would have fields like symbol, start date, and end date. You can find the standard query params inside the `./models` folder. -- **Data** : Are outputs. Stock market data would be StockPriceData and have fields such as Open, High, Low, Close, and Volume. You can find the standard data models inside the `./models` folder. -- **Fetcher** : A provider-specific object (FMPStockPriceFetcher) that holds executable logic that performs the standardization procedure going from QueryParams to the Data. -- **Provider** : An object that is specific to a data provider (i.e. PolygonProvider) which holds Provider attributes and unites all of the provider's fetchers into a list. -- **Provider Registry** : An aggregation of all the Provider objects and their API keys. It is the main point of usage and is responsible for dynamically serving the data by finding the correct Provider and Fetcher based on the given QueryParams. +- **QueryParams** : The input model for a particular query. To load stock market data, we would have StockQueryParams, which would have fields like symbol, start date, and end date. You can find the standard query params inside the `./models` folder. +- **Data** : The output model of a particular query. Stock market data would be StockPriceData and have fields such as Open, High, Low, Close, and Volume. You can find the standard data models inside the `./models` folder. +- **Fetcher** : Class containing a set of methods to receive query parameters, extract data and transform it, if necessary. +- **Provider** : Entry point class for each provider extension. Contains information about the provider, it's required credentials and available fetchers. +- **RegistryLoader** : Loads the registry with the provider extensions installed. +- **Registry** : Maintains a registry of provider extensions installed. +- **RegistryMap** : Stores the complete characterization of each provider. It centralizes information like required credentials, standardised and extra query parameters/data by provider. +- **QueryExecutor** : Executes a given query, routing it to the respective provider and fetcher. ## Installation diff --git a/openbb_sdk/sdk/provider/openbb_provider/__init__.py b/openbb_sdk/sdk/provider/openbb_provider/__init__.py index 49756b8b981..c69b35d1eec 100644 --- a/openbb_sdk/sdk/provider/openbb_provider/__init__.py +++ b/openbb_sdk/sdk/provider/openbb_provider/__init__.py @@ -1,2 +1,3 @@ """OpenBB Provider Package.""" -from . import metadata, models # noqa: F401 +from . import standard_models # noqa: F401 +from .utils import descriptions, helpers # noqa: F401 diff --git a/openbb_sdk/sdk/provider/openbb_provider/abstract/data.py b/openbb_sdk/sdk/provider/openbb_provider/abstract/data.py index 10a95d2fe79..17f67b05c60 100644 --- a/openbb_sdk/sdk/provider/openbb_provider/abstract/data.py +++ b/openbb_sdk/sdk/provider/openbb_provider/abstract/data.py @@ -10,10 +10,3 @@ class Data(BaseModel): class Config: extra = Extra.allow allow_population_by_field_name = True - - -class QueryParams(BaseModel): - """The OpenBB Standardized QueryParams Model that holds the query input parameters.""" - - def __repr__(self): - return f"{self.__class__.__name__}({', '.join([f'{k}={v}' for k, v in self.dict().items()])})" diff --git a/openbb_sdk/sdk/provider/openbb_provider/abstract/fetcher.py b/openbb_sdk/sdk/provider/openbb_provider/abstract/fetcher.py index 383968c87ef..7427c96356a 100644 --- a/openbb_sdk/sdk/provider/openbb_provider/abstract/fetcher.py +++ b/openbb_sdk/sdk/provider/openbb_provider/abstract/fetcher.py @@ -1,22 +1,21 @@ """Abstract class for the fetcher.""" -from typing import Dict, Generic, List, Optional, TypeVar, Union +from typing import Any, Dict, Generic, List, Optional, TypeVar, Union -from openbb_provider.abstract.data import Data, QueryParams +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams QueryParamsType = TypeVar("QueryParamsType", bound=QueryParams) DataType = TypeVar("DataType", bound=Data) ProviderQueryParamsType = TypeVar("ProviderQueryParamsType", bound=QueryParams) ProviderDataType = TypeVar("ProviderDataType", bound=Data) ReturnType = Union[List[ProviderDataType], ProviderDataType] -UDataType = Union[List[DataType], DataType] +GenericDataType = Union[DataType, List[DataType], Dict[str, DataType]] class Fetcher( Generic[ - QueryParamsType, - DataType, ProviderQueryParamsType, ProviderDataType, ] @@ -24,54 +23,43 @@ class Fetcher( """Abstract class for the fetcher.""" @staticmethod - def transform_query( - query: QueryParamsType, - extra_params: Optional[Dict] = None, - ) -> ProviderQueryParamsType: - """Transform the standard query to the provider-specific query.""" - raise NotImplementedError("Method `transform_query` not implemented.") + def transform_query(params: Dict[str, Any]) -> ProviderQueryParamsType: + """Transform the params to the provider-specific query.""" + raise NotImplementedError @staticmethod def extract_data( query: ProviderQueryParamsType, credentials: Optional[Dict[str, str]] ) -> ReturnType: """Extract the data from the provider.""" - raise NotImplementedError("Method `extract_data` not implemented.") + raise NotImplementedError @staticmethod - def transform_data(data: ReturnType) -> UDataType: - """Transform the provider-specific data to the standard data.""" - raise NotImplementedError("Method `transform_query` not implemented.") + def transform_data(data: ReturnType) -> GenericDataType: + """Transform the provider-specific data.""" + raise NotImplementedError @classmethod def fetch_data( cls, - query: QueryParamsType, - extra_params: Optional[Dict], + params: Dict[str, Any], credentials: Optional[Dict[str, str]] = None, - ) -> UDataType: - """Fetch data from a provider by using the OpenBB standard.""" - provider_query = cls.transform_query(query=query, extra_params=extra_params) - provider_data = cls.extract_data(query=provider_query, credentials=credentials) - data = cls.transform_data(data=provider_data) - return data + **kwargs + ) -> GenericDataType: + """Fetch data from a provider.""" + query = cls.transform_query(params=params) + data = cls.extract_data(query=query, credentials=credentials, **kwargs) + results = cls.transform_data(data=data) + return results - @classmethod - def get_query_type(cls): - """Get the type of the query.""" - return cls.transform_query.__annotations__["query"] - - @classmethod - def get_data_type(cls): - """Get the type of the data.""" - return cls.transform_data.__annotations__["return"] - - @classmethod - def get_provider_query_type(cls): + @property + def provider_query_params_type(self): """Get the type of the provider query.""" - return cls.extract_data.__annotations__["query"] + # pylint: disable=E1101 + return self.__orig_bases__[0].__args__[0] - @classmethod - def get_provider_data_type(cls): + @property + def provider_data_type(self): """Get the type of the provider data.""" - return cls.extract_data.__annotations__["return"] + # pylint: disable=E1101 + return self.__orig_bases__[0].__args__[1] diff --git a/openbb_sdk/sdk/provider/openbb_provider/abstract/provider.py b/openbb_sdk/sdk/provider/openbb_provider/abstract/provider.py index f5ef9d59efe..bb3f5d00b99 100644 --- a/openbb_sdk/sdk/provider/openbb_provider/abstract/provider.py +++ b/openbb_sdk/sdk/provider/openbb_provider/abstract/provider.py @@ -1,44 +1,44 @@ """Abstract class for providers.""" -from typing import Any, Dict, List, Optional +from typing import Dict, List, Optional, Type from openbb_provider.abstract.fetcher import Fetcher class Provider: - """Abstract class for providers.""" - - QUERIES = ["get_query_type", "get_provider_query_type"] + """This class serves as provider extensions entry point and must be + created by each provider.""" def __init__( self, name: str, description: str, - required_credentials: Optional[List[str]], - fetcher_list: List[Fetcher], + website: Optional[str] = None, + required_credentials: Optional[List[str]] = None, + fetcher_dict: Optional[Dict[str, Type[Fetcher]]] = None, ) -> None: """Initialize the provider. Parameters ---------- name : str - The name of the provider. + Name of the provider. description : str - The description of the provider. - required_credentials : Optional[List[str]] - The list of required credentials for the provider. - For example, ["api_key", "api_secret"] which would be used inside - the fetcher to get the credentials from the credentials dict. - For example credentials.get("PROVIDER_API_KEY"). - fetcher_list : List[Fetcher] - The list of fetchers that the provider supports. + Description of the provider. + website : Optional[str] + Website of the provider, by default None. + required_credentials : Optional[List[str]], optional + List of required credentials, by default None + fetcher_dict : Optional[Dict[str, Type[Fetcher]]] + Dictionary of fetchers, by default None. """ self.name = name self.description = description - self.required_credentials = required_credentials - the_dict: Dict[str, Any] = {} - for query in self.QUERIES: - the_dict[query] = {getattr(x, query)().__name__: x for x in fetcher_list} - self.fetcher_dict = the_dict - # This is used only for documentation generation - self.fetcher_list = fetcher_list + self.website = website + self.fetcher_dict = fetcher_dict or {} + if required_credentials is None: + self.required_credentials: List = [] + else: + self.required_credentials = [] + for rq in required_credentials: + self.required_credentials.append(f"{self.name.lower()}_{rq}") diff --git a/openbb_sdk/sdk/provider/openbb_provider/abstract/query_params.py b/openbb_sdk/sdk/provider/openbb_provider/abstract/query_params.py new file mode 100644 index 00000000000..5d9bb9229e5 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/abstract/query_params.py @@ -0,0 +1,11 @@ +from pydantic import BaseModel + + +class QueryParams(BaseModel): + """The OpenBB Standardized QueryParams Model that holds the query input parameters.""" + + class Config: + allow_population_by_field_name = True + + def __repr__(self): + return f"{self.__class__.__name__}({', '.join([f'{k}={v}' for k, v in self.dict().items()])})" diff --git a/openbb_sdk/sdk/provider/openbb_provider/map.py b/openbb_sdk/sdk/provider/openbb_provider/map.py deleted file mode 100644 index f1dde7ec3dc..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/map.py +++ /dev/null @@ -1,337 +0,0 @@ -"""Create a detailed Map of the Provider Registry.""" -from typing import Dict, Tuple - -from openbb_provider.registry import provider_registry - - -def _get_data_type(fetcher) -> str: - """Get the data type of the fetcher.""" - data_type = ( - str(fetcher.get_data_type()) # pylint: disable=use-maxsplit-arg - .split(".")[-1] - .replace("]", "") - .replace("'>", "") - ) - return data_type - - -def _get_query_type(fetcher) -> str: - """Get the query type of the fetcher.""" - query_type = ( - str(fetcher.get_query_type()) # pylint: disable=use-maxsplit-arg - .split(".")[-1] - .replace("'>", "") - ) - return query_type - - -def _get_data_fields(fetcher) -> Tuple[Dict, Dict]: - """Get the standard and provider-specific data fields of the fetcher. - - Parameters - ---------- - fetcher : Fetcher - The fetcher to get the data fields from. - - Returns - ------- - Tuple[Dict, Dict] - A tuple of the provider-specific and standardized data fields. - """ - try: - provider_fields = (fetcher.get_provider_data_type()).__fields__ - except AttributeError: - provider_fields = (fetcher.get_provider_data_type().__args__[0]).__fields__ - try: - standard_fields = (fetcher.get_data_type()).__fields__ - except AttributeError: - standard_fields = (fetcher.get_data_type().__args__[0]).__fields__ - - return provider_fields, standard_fields - - -def _extract_fields( - provider_fields: Dict, - standard_fields: Dict, -) -> Tuple[Dict, Dict]: - """Extract the fields and their details from pydantic models. - - Parameters - ---------- - provider_fields : Dict - The fields of the provider pydantic model. - standard_fields : Dict - The fields of the standardized pydantic model. - - Returns - ------- - Tuple[Dict, Dict] - A tuple of the provider fields and standardized fields. - """ - provider_dict: Dict = {} - standard_dict: Dict = {} - - for field in provider_fields: - provider_dict[provider_fields[field].alias] = { - "type": provider_fields[field].annotation, - "default": provider_fields[field].default, - "required": provider_fields[field].required, - } - - for field in standard_fields: - standard_dict[standard_fields[field].alias] = { - "type": standard_fields[field].annotation, - "default": standard_fields[field].default, - "required": standard_fields[field].required, - } - - return provider_dict, standard_dict - - -def build_query_params_mapping() -> Dict: - """Build a mapping of the provider registry QueryParams. - - Returns - ------- - Dict : - The mapping is a nested dictionary with the following structure: - { - "QueryParamsType": { - "provider_name": { - "param_name": { - "type": "param_type", - "default": "param_default", - "required": "param_required" - } - } - "openbb": { - "param_name": { - "type": "param_type", - "default": "param_default", - "required": "param_required" - } - } - } - } - """ - provider_registry_mapping = provider_registry.providers - mapping = {} - for provider in provider_registry_mapping.values(): - for fetcher in provider.fetcher_list: - provider_name = provider.name - - query_type = _get_query_type(fetcher) - - provider_param_fields = (fetcher.get_provider_query_type()).__fields__ - standard_param_fields = (fetcher.get_query_type()).__fields__ - - provider_specific_query_params, standardized_query_params = _extract_fields( - provider_param_fields, - standard_param_fields, - ) - - if query_type not in mapping: - mapping[query_type] = {provider_name: provider_specific_query_params} - mapping[query_type]["openbb"] = standardized_query_params # type: ignore - else: - mapping[query_type][provider_name] = provider_specific_query_params - - return mapping - - -def build_data_mapping() -> Dict: - """Build a mapping of the provider registry Data. - - Returns - ------- - Dict : - The mapping is a nested dictionary with the following structure: - { - "DataType": { - "provider_name": { - "data_field_name": { - "type": "param_type", - "default": "param_default", - "required": "param_required" - } - } - "openbb": { - "data_field_name": { - "type": "param_type", - "default": "param_default", - "required": "param_required" - } - } - } - } - """ - provider_registry_mapping = provider_registry.providers - mapping = {} - for provider in provider_registry_mapping.values(): - for fetcher in provider.fetcher_list: - provider_name = provider.name - - data_type = _get_data_type(fetcher) - - provider_data_fields, standard_data_fields = _get_data_fields(fetcher) - - provider_specific_data_fields, standardized_data_fields = _extract_fields( - provider_data_fields, - standard_data_fields, - ) - - if data_type not in mapping: - mapping[data_type] = {provider_name: provider_specific_data_fields} - mapping[data_type]["openbb"] = standardized_data_fields # type: ignore - else: - mapping[data_type][provider_name] = provider_specific_data_fields - - return mapping - - -def _get_data_docstring(fetcher) -> Tuple[str, str]: - """Get the data docstring of the fetcher for the provider and standardized data. - - Parameters - ---------- - fetcher : Fetcher - The fetcher to get the data docstring from. - - Returns - ------- - Tuple[str, str] - A tuple of the provider and standardized data docstrings. - """ - provider_data_docstring = (fetcher.get_provider_data_type()).__doc__ - if not provider_data_docstring: - try: - provider_data_docstring = ( - fetcher.get_provider_data_type().__args__[0] - ).__doc__ - except AttributeError: - provider_data_docstring = "" - - standard_data_docstring = (fetcher.get_data_type()).__doc__ - if not standard_data_docstring: - try: - standard_data_docstring = (fetcher.get_data_type().__args__[0]).__doc__ - except AttributeError: - standard_data_docstring = "" - - return provider_data_docstring, standard_data_docstring - - -def build_docstring_mapping() -> Dict: - """Build a mapping of the provider registry docstrings. - - Returns - ------- - Dict : - The mapping is a nested dictionary with the following structure: - { - "DataType": { - "provider_name": "docstring", - "openbb": "docstring" - }, - "QueryParamsType": { - "provider_name": "docstring", - "openbb": "docstring" - } - } - """ - mapping = {} - provider_registry_mapping = provider_registry.providers - for provider in provider_registry_mapping.values(): - for fetcher in provider.fetcher_list: - provider_name = provider.name - - data_type = _get_data_type(fetcher) - query_type = _get_query_type(fetcher) - - provider_query_docstring = (fetcher.get_provider_query_type()).__doc__ - standard_query_docstring = (fetcher.get_query_type()).__doc__ - - provider_data_docstring, standard_data_docstring = _get_data_docstring( - fetcher - ) - - if data_type not in mapping: - mapping[data_type] = { - provider_name: provider_data_docstring, - "openbb": standard_data_docstring, - } - else: - mapping[data_type][provider_name] = provider_data_docstring - - if query_type not in mapping: - mapping[query_type] = { - provider_name: provider_query_docstring, - "openbb": standard_query_docstring, - } - else: - mapping[query_type][provider_name] = provider_query_docstring - - return mapping - - -def build_credentials_mapping() -> Dict: - """Build a mapping of the provider registry credentials. - - Returns - ------- - Dict : - The mapping is a nested dictionary with the following structure: - { - "provider_name": [ - "credential_name", - ... - ] - } - ... - """ - mapping = {} - provider_registry_credentials = provider_registry.credentials - for provider, provider_credentials in provider_registry_credentials.items(): - mapping[provider] = list(provider_credentials.keys()) - return mapping - - -def build_model_mapping() -> Dict: - """Build a mapping of the provider registry models. - - Returns - ------- - Dict : - The mapping is a nested dictionary of data and query mappings. - """ - mapping: Dict = {} - data_mapping = build_data_mapping() - query_mapping = build_query_params_mapping() - docstring_mapping = build_docstring_mapping() - - for query_type, providers in query_mapping.items(): - query_type_name = query_type.replace("QueryParams", "") - mapping[query_type_name] = {} - for provider, fields in providers.items(): - mapping[query_type_name][provider] = { - "QueryParams": {}, - "Data": {}, - } - mapping[query_type_name][provider]["QueryParams"] = { - "fields": fields, - "docstring": docstring_mapping[query_type][provider], - } - - for data_type, providers in data_mapping.items(): - data_type_name = data_type.replace("Data", "") - - for provider, fields in providers.items(): - mapping[data_type_name][provider]["Data"] = { - "fields": fields, - "docstring": docstring_mapping[data_type][provider], - } - - return mapping - - -model_mapping = build_model_mapping() diff --git a/openbb_sdk/sdk/provider/openbb_provider/metadata.py b/openbb_sdk/sdk/provider/openbb_provider/metadata.py deleted file mode 100644 index 3658bc03f54..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/metadata.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Metadata for the parameters of the Pydantic models.""" - -DESCRIPTIONS = { - "symbol": "Symbol to get data for.", - "start_date": "Start date of the data.", - "end_date": "End date of the data.", - "weekly": "Whether to return weekly data.", - "monthly": "Whether to return monthly data.", - "period": "Period of the data to return (quarterly or annually).", - "date": "A specific date to get data for.", -} diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/analyst_estimates.py b/openbb_sdk/sdk/provider/openbb_provider/models/analyst_estimates.py deleted file mode 100644 index c667e1dfd1c..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/analyst_estimates.py +++ /dev/null @@ -1,100 +0,0 @@ -"""Analyst estimates data model.""" - - -from datetime import date as dateType -from typing import Literal - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class AnalystEstimatesQueryParams(QueryParams, BaseSymbol): - """Analyst Estimates query. - - Parameter - --------- - symbol : str - The symbol of the company. - period : Literal["quarter", "annual"] - The period of the analyst estimates. - limit : int - The limit of the analyst estimates. - """ - - period: Literal["quarter", "annual"] = "annual" - limit: int = 30 - - -class AnalystEstimatesData(Data, BaseSymbol): - """Analyst estimates data. - - Returns - ------- - symbol : str - The symbol of the asset. - date : date - The date of the analyst estimates. - estimated_revenue_low : int - The estimated revenue low of the analyst estimates. - estimated_revenue_high : int - The estimated revenue high of the analyst estimates. - estimated_revenue_avg : int - The estimated revenue average of the analyst estimates. - estimated_ebitda_low : int - The estimated EBITDA low of the analyst estimates. - estimated_ebitda_high : int - The estimated EBITDA high of the analyst estimates. - estimated_ebitda_avg : int - The estimated EBITDA average of the analyst estimates. - estimated_ebit_low : int - The estimated EBIT low of the analyst estimates. - estimated_ebit_high : int - The estimated EBIT high of the analyst estimates. - estimated_ebit_avg : int - The estimated EBIT average of the analyst estimates. - estimated_net_income_low : int - The estimated net income low of the analyst estimates. - estimated_net_income_high : int - The estimated net income high of the analyst estimates. - estimated_net_income_avg : int - The estimated net income average of the analyst estimates. - estimated_sga_expense_low : int - The estimated SGA expense low of the analyst estimates. - estimated_sga_expense_high : int - The estimated SGA expense high of the analyst estimates. - estimated_sga_expense_avg : int - The estimated SGA expense average of the analyst estimates. - estimated_eps_avg : float - The estimated EPS average of the analyst estimates. - estimated_eps_high : float - The estimated EPS high of the analyst estimates. - estimated_eps_low : float - The estimated EPS low of the analyst estimates. - number_analyst_estimated_revenue : int - The number of analysts who estimated revenue of the analyst estimates. - number_analysts_estimated_eps : int - The number of analysts who estimated EPS of the analyst estimates. - """ - - symbol: str - date: dateType - estimated_revenue_low: int - estimated_revenue_high: int - estimated_revenue_avg: int - estimated_ebitda_low: int - estimated_ebitda_high: int - estimated_ebitda_avg: int - estimated_ebit_low: int - estimated_ebit_high: int - estimated_ebit_avg: int - estimated_net_income_low: int - estimated_net_income_high: int - estimated_net_income_avg: int - estimated_sga_expense_low: int - estimated_sga_expense_high: int - estimated_sga_expense_avg: int - estimated_eps_avg: float - estimated_eps_high: float - estimated_eps_low: float - number_analyst_estimated_revenue: int - number_analysts_estimated_eps: int diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/available_indices.py b/openbb_sdk/sdk/provider/openbb_provider/models/available_indices.py deleted file mode 100644 index 3fa735288f5..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/available_indices.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Available Indices data model.""" - - -from typing import Optional - -from openbb_provider.abstract.data import Data, QueryParams - - -class AvailableIndicesQueryParams(QueryParams): - """Available Indices query.""" - - -class AvailableIndicesData(Data): - """Available Indices data. - - Returns the major indices from Dow Jones, Nasdaq and, S&P 500. - - Returns - ------- - symbol : str - The symbol of the index. - name : Optional[str] - The name of the index. - currency : Optional[str] - The currency the index is traded in. - stock_exchange : str - The stock exchange where the index is listed. - exchange_short_name : str - The short name of the stock exchange where the index is listed. - """ - - symbol: str - name: Optional[str] - currency: Optional[str] - stock_exchange: str - exchange_short_name: str diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/balance_sheet.py b/openbb_sdk/sdk/provider/openbb_provider/models/balance_sheet.py deleted file mode 100644 index d694006ad23..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/balance_sheet.py +++ /dev/null @@ -1,117 +0,0 @@ -"""Balance Sheet Data Model.""" - - -from datetime import date as dateType -from typing import Optional - -from openbb_provider.abstract.data import Data -from openbb_provider.models.base import FinancialStatementQueryParams - -# IMPORT THIRD PARTY - - -class BalanceSheetQueryParams(FinancialStatementQueryParams): - """Cash flow statement query. - - Parameter - --------- - symbol : str - The symbol of the company. - period : Literal["annually", "quarterly"] - The period of the balance sheet. - """ - - -class BalanceSheetData(Data): - """Return Balance Sheet Data. - - Returns - ------- - date : date - The date of the balance sheet. - cik : Optional[int] - The CIK of the company. - cash_and_cash_equivalents : Optional[int] - The cash and cash equivalents of the balance sheet. - short_term_investments : Optional[int] - The short term investments of the balance sheet. - cash_and_short_term_investments : Optional[int] - The cash and short term investments of the balance sheet. - net_receivables : Optional[int] - The net receivables of the balance sheet. - inventory : Optional[int] - The inventory of the balance sheet. - other_current_assets : Optional[int] - The other current assets of the balance sheet. - current_assets : Optional[int] - The current assets of the balance sheet. - long_term_investments : Optional[int] - The long term investments of the balance sheet. - property_plant_equipment_net : Optional[int] - The property plant equipment net of the balance sheet. - other_non_current_assets : Optional[int] - The other non current assets of the balance sheet. - noncurrent_assets : Optional[int] - The non current assets of the balance sheet. - assets : Optional[int] - The assets of the balance sheet. - accounts_payable : Optional[int] - The accounts payable of the balance sheet. - other_current_liabilities : Optional[int] - The other current liabilities of the balance sheet. - deferred_revenue : Optional[int] - The deferred revenue of the balance sheet. - current_liabilities : Optional[int] - The current liabilities of the balance sheet. - long_term_debt : Optional[int] - The long term debt of the balance sheet. - other_non_current_liabilities : Optional[int] - The other non current liabilities of the balance sheet. - noncurrent_liabilities : Optional[int] - The non current liabilities of the balance sheet. - liabilities : Optional[int] - The liabilities of the balance sheet. - common_stock : Optional[int] - The common stock of the balance sheet. - retained_earnings : Optional[int] - The retained earnings of the balance sheet. - accumulated_other_comprehensive_income_loss : Optional[int] - The accumulated other comprehensive income loss of the balance sheet. - total_equity : Optional[int] - The total equity of the balance sheet. - total_liabilities_and_stockholders_equity : Optional[int] - The total liabilities and stockholders equity of the balance sheet. - """ - - date: dateType - cik: Optional[int] - - cash_and_cash_equivalents: Optional[int] - short_term_investments: Optional[int] - cash_and_short_term_investments: Optional[int] - net_receivables: Optional[int] - inventory: Optional[int] - other_current_assets: Optional[int] - current_assets: Optional[int] - - long_term_investments: Optional[int] - property_plant_equipment_net: Optional[int] - other_non_current_assets: Optional[int] - noncurrent_assets: Optional[int] - assets: Optional[int] - - account_payables: Optional[int] - other_current_liabilities: Optional[int] - deferred_revenue: Optional[int] - current_liabilities: Optional[int] - - long_term_debt: Optional[int] - other_non_current_liabilities: Optional[int] - noncurrent_liabilities: Optional[int] - liabilities: Optional[int] - - common_stock: Optional[int] - retained_earnings: Optional[int] - accumulated_other_comprehensive_income_loss: Optional[int] - total_equity: Optional[int] - total_liabilities_and_stockholders_equity: Optional[int] diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/base.py b/openbb_sdk/sdk/provider/openbb_provider/models/base.py deleted file mode 100644 index 2825b518e86..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/base.py +++ /dev/null @@ -1,20 +0,0 @@ -from typing import Literal - -from pydantic import BaseModel, Field, validator - -from openbb_provider.abstract.data import QueryParams -from openbb_provider.metadata import DESCRIPTIONS - - -class BaseSymbol(BaseModel): - symbol: str = Field(min_length=1, description=DESCRIPTIONS.get("symbol", "")) - - @validator("symbol", pre=True) - def upper_symbol(cls, v: str): # pylint: disable=E0213 - return v.upper() - - -class FinancialStatementQueryParams(QueryParams, BaseSymbol): - period: Literal["annually", "quarterly"] = Field( - default="annually", description=DESCRIPTIONS.get("period", "") - ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/cash_flows.py b/openbb_sdk/sdk/provider/openbb_provider/models/cash_flows.py deleted file mode 100644 index 41b6e14e3fe..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/cash_flows.py +++ /dev/null @@ -1,117 +0,0 @@ -"""Cash Flow Statement Data Model.""" - - -from datetime import date as dateType -from typing import Optional - -from openbb_provider.abstract.data import Data -from openbb_provider.models.base import FinancialStatementQueryParams - - -class CashFlowStatementQueryParams(FinancialStatementQueryParams): - """Cash flow statement query. - - Parameter - --------- - symbol : str - The symbol of the company. - period : Literal["annually", "quarterly"] - The period of the income statement. - """ - - -class CashFlowStatementData(Data): - """Return Cash Flow Statement Data. - - Returns - ------- - date : date - The date of the cash flow statement. - symbol : str - The symbol of the company. - currency : Optional[str] - The currency of the cash flow statement. - cik : Optional[int] - The Central Index Key (CIK) of the company. - period : Optional[str] - The period of the cash flow statement. - cash_at_beginning_of_period : Optional[int] - The cash at the beginning of the period. - net_income : Optional[int] - The net income. - depreciation_and_amortization : Optional[int] - The depreciation and amortization. - stock_based_compensation : Optional[int] - The stock based compensation. - accounts_receivables : Optional[int] - The accounts receivables. - inventory : Optional[int] - The inventory. - accounts_payables : Optional[int] - The accounts payables. - net_cash_flow_from_operating_activities : Optional[int] - The net cash flow from operating activities. - purchases_of_investments : Optional[int] - The purchases of investments. - sales_maturities_of_investments : Optional[int] - The sales maturities of investments. - investments_in_property_plant_and_equipment : Optional[int] - The investments in property plant and equipment. - net_cash_flow_from_investing_activities : Optional[int] - The net cash flow from investing activities. - dividends_paid : Optional[int] - The dividends paid. - common_stock_repurchased : Optional[int] - The common stock repurchased. - debt_repayment : Optional[int] - The debt repayment. - other_financing_activities : Optional[int] - The other financing activities. - net_cash_flow_from_financing_activities : Optional[int] - The net cash flow from financing activities. - net_cash_flow : Optional[int] - The net cash flow. - cash_at_end_of_period : Optional[int] - The cash at the end of the period. - """ - - date: dateType - symbol: str - currency: Optional[str] = None - cik: Optional[int] - period: Optional[str] - - cash_at_beginning_of_period: Optional[int] - net_income: Optional[int] - depreciation_and_amortization: Optional[int] - stock_based_compensation: Optional[int] - # OTHER - - accounts_receivables: Optional[int] - inventory: Optional[int] - # VENDOR NON-TRADE_RECEIVABLES - # OTHER CURRENT AND NON-CURRENT ASSETS - accounts_payables: Optional[int] - # DEFERRED_REVENUE - # OTHER CURRENT AND NON-CURRENT LIABILITIES - other_investing_activities: Optional[int] - net_cash_flow_from_operating_activities: Optional[int] - - purchases_of_investments: Optional[int] - sales_maturities_of_investments: Optional[int] - investments_in_property_plant_and_equipment: Optional[int] - # OTHER - net_cash_flow_from_investing_activities: Optional[int] - - dividends_paid: Optional[int] - common_stock_repurchased: Optional[int] - debt_repayment: Optional[int] - - other_financing_activities: Optional[int] - # REPAYMENT OF COMMERCIAL PAPER - # OTHER - - net_cash_flow_from_financing_activities: Optional[int] - - net_cash_flow: Optional[int] - cash_at_end_of_period: Optional[int] diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/company_overview.py b/openbb_sdk/sdk/provider/openbb_provider/models/company_overview.py deleted file mode 100644 index 665fa5bbb1c..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/company_overview.py +++ /dev/null @@ -1,135 +0,0 @@ -"""Company Overview Data Model.""" - - -from datetime import date -from typing import Optional - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class CompanyOverviewQueryParams(QueryParams, BaseSymbol): - """Company overview query. - - Parameter - --------- - symbol : str - The symbol of the company. - """ - - -class CompanyOverviewData(Data): - """Company Overview data. - - Returns - ------- - symbol : str - The symbol of the company. - price : float - The price of the company. - beta : float - The beta of the company. - vol_avg : int - The volume average of the company. - mkt_cap : int - The market capitalization of the company. - last_div : float - The last dividend of the company. - range : str - The range of the company. - changes : float - The changes of the company. - company_name : str - The company name of the company. - currency : str - The currency of the company. - cik : Optional[str] - The CIK of the company. - isin : Optional[str] - The ISIN of the company. - cusip : Optional[str] - The CUSIP of the company. - exchange : str - The exchange of the company. - exchange_short_name : str - The exchange short name of the company. - industry : str - The industry of the company. - website : str - The website of the company. - description : str - The description of the company. - ceo : str - The CEO of the company. - sector : str - The sector of the company. - country : str - The country of the company. - full_time_employees : str - The full time employees of the company. - phone : str - The phone of the company. - address : str - The address of the company. - city : str - The city of the company. - state : str - The state of the company. - zip : str - The zip of the company. - dcf_diff : float - The discounted cash flow difference of the company. - dcf : float - The discounted cash flow of the company. - image : str - The image of the company. - ipo_date : date - The IPO date of the company. - default_image : bool - If the image is the default image. - is_etf : bool - If the company is an ETF. - is_actively_trading : bool - If the company is actively trading. - is_adr : bool - If the company is an ADR. - is_fund : bool - If the company is a fund. - """ - - symbol: str - price: float - beta: float - vol_avg: int - mkt_cap: int - last_div: float - range: str - changes: float - company_name: str - currency: str - cik: Optional[str] - isin: Optional[str] - cusip: Optional[str] - exchange: str - exchange_short_name: str - industry: str - website: str - description: str - ceo: str - sector: str - country: str - full_time_employees: str - phone: str - address: str - city: str - state: str - zip: str - dcf_diff: float - dcf: float - image: str - ipo_date: date - default_image: bool - is_etf: bool - is_actively_trading: bool - is_adr: bool - is_fund: bool diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/cpi.py b/openbb_sdk/sdk/provider/openbb_provider/models/cpi.py deleted file mode 100644 index a20ad37a2e9..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/cpi.py +++ /dev/null @@ -1,53 +0,0 @@ -from datetime import date as dateType -from typing import Dict, List, Optional - -from openbb_fred.utils.fred_helpers import CPI_COUNTRIES, CPI_FREQUENCY, CPI_UNITS -from pydantic import BaseModel, validator - -from openbb_provider.abstract.data import Data, QueryParams - - -class CPIQueryParams(QueryParams): - """CPI query. - When other provders are added, this will probably need less strict types - - Parameter - --------- - countries: List[CPI_COUNTRIES] - The country or countries you want to see. - units: List[CPI_UNITS] - The units you want to see, can be "growth_previous", "growth_same" or "index_2015". - frequency: List[CPI_FREQUENCY] - The frequency you want to see, either "annual", monthly" or "quarterly". - harmonized: bool - Whether you wish to obtain harmonized data. - start_date: Optional[date] - Start date, formatted YYYY-MM-DD - end_date: Optional[date] - End date, formatted YYYY-MM-DD - """ - - countries: List[CPI_COUNTRIES] - units: CPI_UNITS = "growth_same" - frequency: CPI_FREQUENCY = "monthly" - harmonized: bool = False - start_date: Optional[dateType] - end_date: Optional[dateType] - - -class CPIDataPoint(BaseModel): - realtime_start: dateType - realtime_end: dateType - date: dateType - value: float - - @validator("value", pre=True) - def value_validate(cls, v: str): # pylint: disable=E0213 - if v == ".": - return 0.0 - return float(v) - - -class CPIData(Data): - # I don't love this, but the keys are dynamic based on input so I don't know another way - data: Dict[str, List[CPIDataPoint]] diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/crypto_eod.py b/openbb_sdk/sdk/provider/openbb_provider/models/crypto_eod.py deleted file mode 100644 index 8739fca3508..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/crypto_eod.py +++ /dev/null @@ -1,63 +0,0 @@ -"""Crypto aggregate end of day price data model.""" - - -from datetime import date, datetime -from typing import Optional - -from pydantic import Field, PositiveFloat - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.metadata import DESCRIPTIONS -from openbb_provider.models.base import BaseSymbol - - -class CryptoEODQueryParams(QueryParams, BaseSymbol): - """Crypto end of day query. - - Parameter - --------- - symbol : str - The symbol of the company. - start_date : Optional[date] - The start date of the stock data from which to retrieve the data. - end_date : Optional[date] - The end date of the stock data up to which to retrieve the data. - """ - - start_date: Optional[date] = Field( - description=DESCRIPTIONS.get("start_date", ""), - default=None, - ) - end_date: Optional[date] = Field( - description=DESCRIPTIONS.get("end_date", ""), default=None - ) - - -class CryptoEODData(Data): - """Crypto end of day price data. - - Returns - ------- - date : datetime - The date of the stock. - open : PositiveFloat - The open price of the stock. - high : PositiveFloat - The high price of the stock. - low : PositiveFloat - The low price of the stock. - close : PositiveFloat - The close price of the stock. - adj_close : Optional[PositiveFloat] - The adjusted close price of the stock. - volume : PositiveFloat - The volume of the stock. - """ - - date: datetime - open: PositiveFloat - high: PositiveFloat - low: PositiveFloat - close: PositiveFloat - adj_close: Optional[PositiveFloat] - volume: PositiveFloat diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/crypto_price.py b/openbb_sdk/sdk/provider/openbb_provider/models/crypto_price.py deleted file mode 100644 index 45084ed4fc5..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/crypto_price.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Crypto Price Data Model.""" - - -from datetime import date, datetime -from typing import Optional - -from pydantic import Field, PositiveFloat - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.metadata import DESCRIPTIONS -from openbb_provider.models.base import BaseSymbol - - -class CryptoPriceQueryParams(QueryParams, BaseSymbol): - """Crypto price query. - - Parameter - --------- - symbol : str - The symbol of the company. - start_date : date - The start date of the query. - end_date : Optional[date] - The end date of the query. - """ - - # These fields only work with Polygon - start_date: date = Field(description=DESCRIPTIONS.get("start_date", "")) - end_date: Optional[date] = Field( - description=DESCRIPTIONS.get("end_date", ""), default=date.today() - ) - - -class CryptoPriceData(Data): - """Crypto price data. - - Returns - ------- - open : PositiveFloat - The open price of the stock. - high : PositiveFloat - The high price of the stock. - low : PositiveFloat - The low price of the stock. - close : PositiveFloat - The close price of the stock. - date : datetime - The date of the stock. - """ - - open: PositiveFloat - high: PositiveFloat - low: PositiveFloat - close: PositiveFloat - date: datetime diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/dividend_calendar.py b/openbb_sdk/sdk/provider/openbb_provider/models/dividend_calendar.py deleted file mode 100644 index 944c834f478..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/dividend_calendar.py +++ /dev/null @@ -1,73 +0,0 @@ -"""Dividend Calendar data model.""" - - -from datetime import ( - date as dateType, - datetime, - timedelta, -) -from typing import Optional - -from pydantic import Field, NonNegativeFloat - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.metadata import DESCRIPTIONS - - -class DividendCalendarQueryParams(QueryParams): - """Dividend Calendar query. - - The maximum time interval between the start and end date can be 3 months. - Default value for time interval is 1 month. - - Parameter - --------- - start_date : date - The starting date to fetch the dividend calendar from. Default value is the - previous day from the last month. - end_date : date - The ending date to fetch the dividend calendar till. Default value is the - previous day from the current month. - """ - - start_date: Optional[dateType] = Field( - description=DESCRIPTIONS.get("start_date", ""), - default=datetime.now().date() - timedelta(days=31), - ) - end_date: Optional[dateType] = Field( - description=DESCRIPTIONS.get("end_date", ""), - default=datetime.now().date() - timedelta(days=1), - ) - - -class DividendCalendarData(Data): - """Dividend Calendar data. - - Returns - ------- - date : dateType - The date of the dividend in the calendar. - label : str - The date in human readable form in the calendar. - adjDividend : Optional[NonNegativeFloat] - The adjusted dividend on a date in the calendar. - symbol : str - The symbol of the company for which the dividend is returned in the calendar. - dividend : Optional[NonNegativeFloat] - The dividend amount in the calendar. - recordDate : Optional[dateType] - The record date of the dividend in the calendar. - paymentDate : Optional[dateType] - The payment date of the dividend in the calendar. - declarationDate : Optional[dateType] - The declaration date of the dividend in the calendar. - """ - - date: dateType - label: str - adjDividend: Optional[NonNegativeFloat] = Field(alias="adj_dividend") - symbol: str - dividend: Optional[NonNegativeFloat] - recordDate: Optional[dateType] = Field(alias="record_date") - paymentDate: Optional[dateType] = Field(alias="payment_date") - declarationDate: Optional[dateType] = Field(alias="declaration_date") diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/earnings_calendar.py b/openbb_sdk/sdk/provider/openbb_provider/models/earnings_calendar.py deleted file mode 100644 index 0669238fc1c..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/earnings_calendar.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Earnings calendar data model.""" - - -from datetime import date as dateType -from typing import Optional - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class EarningsCalendarQueryParams(QueryParams, BaseSymbol): - """Earnings calendar rating query model. - - Parameter - --------- - symbol : str - The symbol of the company. - limit : int - """ - - limit: Optional[int] - - -class EarningsCalendarData(Data, BaseSymbol): - """Earnings calendar data. - - Returns - ------- - symbol : str - The symbol of the asset. - date : date - The date of the earnings calendar. - eps : float - The EPS of the earnings calendar. - eps_estimated : float - The estimated EPS of the earnings calendar. - time : str - The time of the earnings calendar. - revenue : int - The revenue of the earnings calendar. - revenue_estimated : int - The estimated revenue of the earnings calendar. - updated_from_date : date - The updated from date of the earnings calendar. - fiscal_date_ending : date - The fiscal date ending of the earnings calendar. - """ - - date: dateType - symbol: str - eps: Optional[float] - eps_estimated: Optional[float] - time: str - revenue: Optional[int] - revenue_estimated: Optional[int] - updated_from_date: Optional[dateType] - fiscal_date_ending: dateType diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/earnings_call_transcript.py b/openbb_sdk/sdk/provider/openbb_provider/models/earnings_call_transcript.py deleted file mode 100644 index 620f3f7fc8e..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/earnings_call_transcript.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Earnings call transcript data model.""" - - -from datetime import datetime - -from pydantic import validator - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class EarningsCallTranscriptQueryParams(QueryParams, BaseSymbol): - """Earnings call transcript rating query model. - - Parameter - --------- - symbol : str - The symbol of the company. - year : int - The year of the earnings call transcript. - """ - - year: int - - @validator("year", pre=True) - def time_validate(cls, v: int): # pylint: disable=E0213 - current_year = datetime.now().year - if v > current_year: - return current_year - if v < 1950: - return current_year - return v - - -class EarningsCallTranscriptData(Data, BaseSymbol): - """Earnings call transcript data. - - Returns - ------- - symbol : str - The symbol of the asset. - quarter : int - The quarter of the earnings call transcript. - year : int - The year of the earnings call transcript. - date : datetime - The date of the earnings call transcript. - content : str - The content of the earnings call transcript. - """ - - symbol: str - quarter: int - year: int - date: datetime - content: str diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/executive_compensation.py b/openbb_sdk/sdk/provider/openbb_provider/models/executive_compensation.py deleted file mode 100644 index b011078ec9d..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/executive_compensation.py +++ /dev/null @@ -1,66 +0,0 @@ -"""Executive Compensation Data Model.""" - - -from datetime import date as dateType -from typing import Optional - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class ExecutiveCompensationQueryParams(QueryParams, BaseSymbol): - """Executive Compensation query model. - - Parameter - --------- - symbol : str - The symbol of the company. - """ - - -class ExecutiveCompensationData(Data): - """Return Executive Compensation Data. - - Returns - ------- - cik : Optional[str] - The Central Index Key (CIK) of the company. - symbol : str - The symbol of the company. - filing_date : dateType - The date of the filing. - accepted_date : dateType - The date the filing was accepted. - name_and_position : str - The name and position of the executive. - year : int - The year of the compensation. - salary : float - The salary of the executive. - bonus : float - The bonus of the executive. - stock_award : float - The stock award of the executive. - incentive_plan_compensation : float - The incentive plan compensation of the executive. - all_other_compensation : float - The all other compensation of the executive. - total : float - The total compensation of the executive. - url : str - The URL of the filing. - """ - - cik: Optional[str] - symbol: str - filing_date: dateType - accepted_date: dateType - name_and_position: str - year: int - salary: float - bonus: float - stock_award: float - incentive_plan_compensation: float - all_other_compensation: float - total: float - url: str diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/forex_eod.py b/openbb_sdk/sdk/provider/openbb_provider/models/forex_eod.py deleted file mode 100644 index a51aca640a5..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/forex_eod.py +++ /dev/null @@ -1,48 +0,0 @@ -"""Forex aggregate end of day price data model.""" - - -from datetime import datetime -from typing import Optional - -from pydantic import NonNegativeFloat, PositiveFloat - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class ForexEODQueryParams(QueryParams, BaseSymbol): - """Forex end of day query. - - Parameter - --------- - symbol : str - The symbol of the forex currency pair. - """ - - -class ForexEODData(Data): - """Forex end of day price data. - - Returns - ------- - date : datetime - The date of the forex currency pair. - open : PositiveFloat - The open price of the forex currency pair. - high : PositiveFloat - The high price of the forex currency pair. - low : PositiveFloat - The low price of the forex currency pair. - close : PositiveFloat - The close price of the forex currency pair. - adj_close : Optional[PositiveFloat] - The adjusted close price of the forex currency pair. - """ - - date: datetime - open: PositiveFloat - high: PositiveFloat - low: PositiveFloat - close: PositiveFloat - adj_close: Optional[PositiveFloat] - volume: NonNegativeFloat diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/forex_pairs.py b/openbb_sdk/sdk/provider/openbb_provider/models/forex_pairs.py deleted file mode 100644 index 269a426ca72..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/forex_pairs.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Forex available pairs data model.""" - - -from typing import Optional - -from openbb_provider.abstract.data import Data, QueryParams - - -class ForexPairsQueryParams(QueryParams): - """Forex available pairs query.""" - - -class ForexPairsData(Data): - """Forex available pairs data. - - Returns - ------- - symbol : str - The symbol of the currency pair. - name : str - The name of the currency pair separated by '/'. - currency : str - The base currency of the currency pair. - stock_exchange : Optional[str] - The stock exchange of the currency pair. - exchange_short_name : Optional[str] - The short name of the stock exchange of the currency pair. - """ - - symbol: str - name: str - currency: str - stock_exchange: Optional[str] - exchange_short_name: Optional[str] diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/forex_price.py b/openbb_sdk/sdk/provider/openbb_provider/models/forex_price.py deleted file mode 100644 index dc7047b6e55..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/forex_price.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Forex Price Data Model.""" - - -from datetime import date, datetime -from typing import Optional - -from pydantic import Field, PositiveFloat - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.metadata import DESCRIPTIONS -from openbb_provider.models.base import BaseSymbol - - -class ForexPriceQueryParams(QueryParams, BaseSymbol): - """Forex price query. - - Parameter - --------- - symbol : str - The symbol of the company. - start_date : date - The start date of the query. - end_date : Optional[date] - The end date of the query. - """ - - # These fields only work with Polygon - start_date: date = Field(description=DESCRIPTIONS.get("start_date", "")) - end_date: Optional[date] = Field( - description=DESCRIPTIONS.get("end_date", ""), default=date.today() - ) - - -class ForexPriceData(Data): - """Forex price data. - - Returns - ------- - open : PositiveFloat - The open price of the stock. - high : PositiveFloat - The high price of the stock. - low : PositiveFloat - The low price of the stock. - close : PositiveFloat - The close price of the stock. - date : datetime - The date of the stock. - """ - - open: PositiveFloat - high: PositiveFloat - low: PositiveFloat - close: PositiveFloat - date: datetime diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/global_news.py b/openbb_sdk/sdk/provider/openbb_provider/models/global_news.py deleted file mode 100644 index 104bcece2ef..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/global_news.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Global News Data Model.""" - - -from datetime import datetime -from typing import Optional - -from pydantic import Field, NonNegativeInt - -from openbb_provider.abstract.data import Data, QueryParams - - -class GlobalNewsQueryParams(QueryParams): - """Global news query. - - Parameter - --------- - page : NonNegativeInt - The page of the global news to be retrieved. - """ - - page: NonNegativeInt = Field(default=0) - - -class GlobalNewsData(Data): - """Return Global News Data. - - Returns - ------- - date : datetime - The published date of the news. - title : str - The title of the news. - image : Optional[str] - The image URL of the news. - text : str - The text/body of the news. - url : str - The URL of the news. - """ - - date: datetime - title: str - image: Optional[str] - text: str - url: str diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/historical_dividends.py b/openbb_sdk/sdk/provider/openbb_provider/models/historical_dividends.py deleted file mode 100644 index 1b36e0b2391..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/historical_dividends.py +++ /dev/null @@ -1,48 +0,0 @@ -"""Historical dividends data model.""" - - -from datetime import date as dateType -from typing import Optional - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class HistoricalDividendsQueryParams(QueryParams, BaseSymbol): - """Historical dividends query. - - Parameter - --------- - symbol : str - The symbol of the company. - """ - - -class HistoricalDividendsData(Data): - """Historical dividends data. - - Returns - ------- - date : date - The date of the historical dividends. - label : str - The label of the historical dividends. - adj_dividend : float - The adjusted dividend of the historical dividends. - dividend : float - The dividend of the historical dividends. - record_date : date - The record date of the historical dividends. - payment_date : date - The payment date of the historical dividends. - declaration_date : date - The declaration date of the historical dividends. - """ - - date: dateType - label: str - adj_dividend: float - dividend: float - record_date: Optional[dateType] - payment_date: Optional[dateType] - declaration_date: Optional[dateType] diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/historical_employees.py b/openbb_sdk/sdk/provider/openbb_provider/models/historical_employees.py deleted file mode 100644 index 4d47a09b8d8..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/historical_employees.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Historical Employees data model.""" - - -from datetime import date, datetime - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class HistoricalEmployeesQueryParams(QueryParams, BaseSymbol): - """Historical Employees query model. - - Parameter - --------- - symbol : str - The symbol of the company. - """ - - -class HistoricalEmployeesData(Data): - """Historical Employees data. - - Returns - ------- - symbol : str - The symbol of the company to retrieve the historical employees of. - cik : int - The CIK of the company to retrieve the historical employees of. - acceptance_time : datetime - The time of acceptance of the company employee. - period_of_report : date - The date of reporting of the company employee. - company_name : str - The registered name of the company to retrieve the historical employees of. - form_type : float - The form type of the company employee. - filing_date : date - The filing date of the company employee - employee_count : int - The count of employees of the company. - source : str - The source URL which retrieves this data for the company. - """ - - symbol: str - cik: int - acceptance_time: datetime - period_of_report: date - company_name: str - form_type: str - filing_date: date - employee_count: int - source: str diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/historical_stock_splits.py b/openbb_sdk/sdk/provider/openbb_provider/models/historical_stock_splits.py deleted file mode 100644 index 17a95b475fc..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/historical_stock_splits.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Historical stock splits data model.""" - - -from datetime import date as dateType - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class HistoricalStockSplitsQueryParams(QueryParams, BaseSymbol): - """Historical Stock Splits query model. - - Parameter - --------- - symbol : str - The symbol of the company. - """ - - -class HistoricalStockSplitsData(Data): - """Historical stock splits data. - - Returns - ------- - date : date - The date of the historical stock splits. - label : str - The label of the historical stock splits. - numerator : float - The numerator of the historical stock splits. - denominator : float - The denominator of the historical stock splits. - """ - - date: dateType - label: str - numerator: float - denominator: float diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/income_statement.py b/openbb_sdk/sdk/provider/openbb_provider/models/income_statement.py deleted file mode 100644 index 3ea229a635a..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/income_statement.py +++ /dev/null @@ -1,119 +0,0 @@ -"""Income Statement Data Model.""" - - -from datetime import date as dateType -from typing import Optional - -from openbb_provider.abstract.data import Data -from openbb_provider.models.base import FinancialStatementQueryParams - - -class IncomeStatementQueryParams(FinancialStatementQueryParams): - """Income statement query. - - Parameter - --------- - symbol : str - The symbol of the company. - period : Literal["annually", "quarterly"] - The period of the income statement. - """ - - -class IncomeStatementData(Data): - """Income Statement Data. - - Returns - ------- - date : date - The date of the income statement. - symbol : str - The symbol of the company. - currency : Optional[str] - The currency of the income statement. - cik : Optional[int] - The Central Index Key (CIK) of the company. - filing_date : date - The filing date of the income statement. - accepted_date : Optional[date] - The accepted date of the income statement. - period : Optional[str] - The period of the income statement. - revenue : Optional[int] - The revenue of the income statement. - cost_of_revenue : Optional[int] - The cost of revenue of the income statement. - gross_profit : Optional[int] - The gross profit of the income statement. - research_and_development_expenses : Optional[int] - The research and development expenses of the income statement. - general_and_administrative_expenses : Optional[int] - The general and administrative expenses of the income statement. - selling_and_marketing_expenses : Optional[float] - The selling and marketing expenses of the income statement. - selling_general_and_administrative_expenses : Optional[int] - The selling, general and administrative expenses of the income statement. - other_expenses : Optional[int] - The other expenses of the income statement. - operating_expenses : Optional[int] - The operating expenses of the income statement. - depreciation_and_amortization : Optional[int] - The depreciation and amortization of the income statement. - ebitda : Optional[int] - The earnings before interest, taxes, depreciation and amortization of the income statement. - operating_income : Optional[int] - The operating income of the income statement. - interest_income : Optional[int] - The interest income of the income statement. - interest_expense : Optional[int] - The interest expense of the income statement. - total_other_income_expenses_net : Optional[int] - The total other income expenses net of the income statement. - income_before_tax : Optional[int] - The income before tax of the income statement. - income_tax_expense : Optional[int] - The income tax expense of the income statement. - net_income : Optional[int] - The net income of the income statement. - eps : Optional[float] - The earnings per share of the income statement. - eps_diluted : Optional[float] - The diluted earnings per share of the income statement. - weighted_average_shares_outstanding : Optional[int] - The weighted average shares outstanding of the income statement. - weighted_average_shares_outstanding_dil : Optional[int] - The weighted average shares outstanding diluted of the income statement. - """ - - date: dateType - currency: Optional[str] = None - filing_date: Optional[dateType] = None - accepted_date: Optional[dateType] = None - period: Optional[str] - - revenue: Optional[int] - cost_of_revenue: Optional[int] - gross_profit: Optional[int] - - research_and_development_expenses: Optional[int] = None - general_and_administrative_expenses: Optional[int] = None - selling_and_marketing_expenses: Optional[float] = None - selling_general_and_administrative_expenses: Optional[int] = None - other_expenses: Optional[int] = None - operating_expenses: Optional[int] - - depreciation_and_amortization: Optional[int] = None - ebitda: Optional[int] = None - operating_income: Optional[int] = None - - interest_income: Optional[int] = None - interest_expense: Optional[int] = None - total_other_income_expenses_net: Optional[int] = None - income_before_tax: Optional[int] - income_tax_expense: Optional[int] - - net_income: Optional[int] - eps: Optional[float] - eps_diluted: Optional[float] - weighted_average_shares_outstanding: Optional[int] = None - weighted_average_shares_outstanding_dil: Optional[int] = None diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/institutional_ownership.py b/openbb_sdk/sdk/provider/openbb_provider/models/institutional_ownership.py deleted file mode 100644 index 1cd5b5cdc32..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/institutional_ownership.py +++ /dev/null @@ -1,127 +0,0 @@ -"""Institutional Ownership Data Model.""" - - -from datetime import date as dateType -from typing import Optional - -from pydantic import Field - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.metadata import DESCRIPTIONS -from openbb_provider.models.base import BaseSymbol - - -class InstitutionalOwnershipQueryParams(QueryParams, BaseSymbol): - """Institutional Ownership QueryParams. - - Parameter - --------- - symbol : str - The symbol of the company. - include_current_quarter : bool - Whether to include the current quarter. Default is False. - date : Optional[dateType] - A specific date to get data for. - """ - - include_current_quarter: bool = Field( - default=False, description="Include current quarter data." - ) - date: Optional[dateType] = Field(description=DESCRIPTIONS.get("date", "")) - - -class InstitutionalOwnershipData(Data): - """Institutional Ownership data. - - Returns - ------- - symbol : str - The symbol of the company. - cik : str - The CIK of the company. - date : dateType - The date of the institutional ownership. - investors_holding : int - The number of investors holding the stock. - last_investors_holding : int - The number of investors holding the stock in the last quarter. - investors_holding_change : int - The change in the number of investors holding the stock. - number_of_13f_shares : int - The number of 13F shares. - last_number_of_13f_shares : int - The number of 13F shares in the last quarter. - number_of_13f_shares_change : int - The change in the number of 13F shares. - total_invested : float - The total amount invested. - last_total_invested : float - The total amount invested in the last quarter. - total_invested_change : float - The change in the total amount invested. - ownership_percent : float - The ownership percent. - last_ownership_percent : float - The ownership percent in the last quarter. - ownership_percent_change : float - The change in the ownership percent. - new_positions : int - The number of new positions. - last_new_positions : int - The number of new positions in the last quarter. - new_positions_change : int - The change in the number of new positions. - increased_positions : int - The number of increased positions. - last_increased_positions : int - The number of increased positions in the last quarter. - increased_positions_change : int - The change in the number of increased positions. - closed_positions : int - The number of closed positions. - last_closed_positions : int - The number of closed positions in the last quarter. - closed_positions_change : int - The change in the number of closed positions. - reduced_positions : int - The number of reduced positions. - last_reduced_positions : int - The number of reduced positions in the last quarter. - """ - - symbol: str = Field(min_lenght=1) - cik: Optional[str] = Field(min_lenght=1) - date: dateType - investors_holding: int - last_investors_holding: int - investors_holding_change: int - number_of_13f_shares: Optional[int] - last_number_of_13f_shares: Optional[int] - number_of_13f_shares_change: Optional[int] - total_invested: float - last_total_invested: float - total_invested_change: float - ownership_percent: float - last_ownership_percent: float - ownership_percent_change: float - new_positions: int - last_new_positions: int - new_positions_change: int - increased_positions: int - last_increased_positions: int - increased_positions_change: int - closed_positions: int - last_closed_positions: int - closed_positions_change: int - reduced_positions: int - last_reduced_positions: int - reduced_positions_change: int - total_calls: int - last_total_calls: int - total_calls_change: int - total_puts: int - last_total_puts: int - total_puts_change: int - put_call_ratio: float - last_put_call_ratio: float - put_call_ratio_change: float diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/key_executives.py b/openbb_sdk/sdk/provider/openbb_provider/models/key_executives.py deleted file mode 100644 index 018cd9bcfca..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/key_executives.py +++ /dev/null @@ -1,58 +0,0 @@ -"""Key Executives Data Model.""" - - -from datetime import datetime -from typing import Optional - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class KeyExecutivesQueryParams(QueryParams, BaseSymbol): - """Key executives query. - - Parameter - --------- - symbol : str - The symbol of the company. - key_executive_name : Optional[str] - The name of the key executive to be retrieved. - key_executive_title : Optional[str] - The title of the key executive to be retrieved. - key_executive_title_since : Optional[datetime] - The period since the position of the key executive to be retrieved. - key_executive_year_born : Optional[datetime] - The year born of the key executive to be retrieved. - key_executive_gender: Optional[str] - The gender of the key executive to be retrieved. - """ - - key_executive_name: Optional[str] - key_executive_title: Optional[str] - key_executive_title_since: Optional[datetime] - key_executive_year_born: Optional[datetime] - key_executive_gender: Optional[str] - - -class KeyExecutivesData(Data): - """Key Executives Data. - - Returns - ------- - name : Optional[str] - The name of the key executive. - title : Optional[str] - The title of the key executive. - title_since : Optional[datetime] - The title since of the key executive. - year_born : Optional[datetime] - The year born of the key executive. - gender : Optional[str] - The gender of the key executive. - """ - - name: Optional[str] - title: Optional[str] - title_since: Optional[datetime] - year_born: Optional[datetime] - gender: Optional[str] diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/key_metrics.py b/openbb_sdk/sdk/provider/openbb_provider/models/key_metrics.py deleted file mode 100644 index 9d0c94c621f..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/key_metrics.py +++ /dev/null @@ -1,210 +0,0 @@ -"""Key Metrics Data Model.""" - - -from datetime import date as DateType -from typing import Literal, Optional - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class KeyMetricsQueryParams(QueryParams, BaseSymbol): - """Key Metrics query model. - - Parameter - --------- - symbol : str - The symbol of the company. - """ - - period: Literal["annually", "quarterly"] = "annually" - limit: Optional[int] = None - - -class KeyMetricsData(Data): - """Return Key Metrics Data. - - Returns - ------- - symbol : str - The symbol of the stock. - date : DateType - The date of the key metrics. - period : str - The period of the key metrics. - revenue_per_share : float - The revenue per share of the stock. - net_income_per_share : float - The net income per share of the stock. - operating_cash_flow_per_share : float - The operating cash flow per share of the stock. - free_cash_flow_per_share : float - The free cash flow per share of the stock. - cash_per_share : float - The cash per share of the stock. - book_value_per_share : float - The book value per share of the stock. - tangible_book_value_per_share : float - The tangible book value per share of the stock. - shareholders_equity_per_share : float - The shareholders equity per share of the stock. - interest_debt_per_share : float - The interest debt per share of the stock. - market_cap : float - The market cap of the stock. - enterprise_value : float - The enterprise value of the stock. - pe_ratio : float - The PE ratio of the stock. - price_to_sales_ratio : float - The price to sales ratio of the stock. - pocf_ratio : float - The POCF ratio of the stock. - pfcf_ratio : float - The PFCF ratio of the stock. - pb_ratio : float - The PB ratio of the stock. - ptb_ratio : float - The PTB ratio of the stock. - ev_to_sales : float - The EV to sales of the stock. - enterprise_value_over_ebitda : float - The enterprise value over EBITDA of the stock. - ev_to_operating_cash_flow : float - The EV to operating cash flow of the stock. - ev_to_free_cash_flow : float - The EV to free cash flow of the stock. - earnings_yield : float - The earnings yield of the stock. - free_cash_flow_yield : float - The free cash flow yield of the stock. - debt_to_equity : float - The debt to equity of the stock. - debt_to_assets : float - The debt to assets of the stock. - net_debt_to_ebitda : float - The net debt to EBITDA of the stock. - current_ratio : float - The current ratio of the stock. - interest_coverage : float - The interest coverage of the stock. - income_quality : float - The income quality of the stock. - dividend_yield : float - The dividend yield of the stock. - payout_ratio : float - The payout ratio of the stock. - sales_general_and_administrative_to_revenue : float - The sales general and administrative to revenue of the stock. - research_and_developement_to_revenue : float - The research and development to revenue of the stock. - intangibles_to_total_assets : float - The intangibles to total assets of the stock. - capex_to_operating_cash_flow : float - The capex to operating cash flow of the stock. - capex_to_revenue : float - The capex to revenue of the stock. - capex_to_depreciation : float - The capex to depreciation of the stock. - stock_based_compensation_to_revenue : float - The stock based compensation to revenue of the stock. - graham_number : float - The graham number of the stock. - roic : float - The ROIC of the stock. - return_on_tangible_assets : float - The return on tangible assets of the stock. - graham_net_net : float - The graham net net of the stock. - working_capital : float - The working capital of the stock. - tangible_asset_value : float - The tangible asset value of the stock. - net_current_asset_value : float - The net current asset value of the stock. - invested_capital : float - The invested capital of the stock. - average_receivables : float - The average receivables of the stock. - average_payables : float - The average payables of the stock. - average_inventory : float - The average inventory of the stock. - days_sales_outstanding : float - The days sales outstanding of the stock. - days_payables_outstanding : float - The days payables outstanding of the stock. - days_of_inventory_on_hand : float - The days of inventory on hand of the stock. - receivables_turnover : float - The receivables turnover of the stock. - payables_turnover : float - The payables turnover of the stock. - inventory_turnover : float - The inventory turnover of the stock. - roe : float - The ROE of the stock. - capex_per_share : float - The capex per share of the stock. - """ - - symbol: str - date: DateType - period: str - revenue_per_share: Optional[float] - net_income_per_share: Optional[float] - operating_cash_flow_per_share: Optional[float] - free_cash_flow_per_share: Optional[float] - cash_per_share: Optional[float] - book_value_per_share: Optional[float] - tangible_book_value_per_share: Optional[float] - shareholders_equity_per_share: Optional[float] - interest_debt_per_share: Optional[float] - market_cap: Optional[float] - enterprise_value: Optional[float] - pe_ratio: Optional[float] - price_to_sales_ratio: Optional[float] - pocf_ratio: Optional[float] - pfcf_ratio: Optional[float] - pb_ratio: Optional[float] - ptb_ratio: Optional[float] - ev_to_sales: Optional[float] - enterprise_value_over_ebitda: Optional[float] - ev_to_operating_cash_flow: Optional[float] - ev_to_free_cash_flow: Optional[float] - earnings_yield: Optional[float] - free_cash_flow_yield: Optional[float] - debt_to_equity: Optional[float] - debt_to_assets: Optional[float] - net_debt_to_ebitda: Optional[float] - current_ratio: Optional[float] - interest_coverage: Optional[float] - income_quality: Optional[float] - dividend_yield: Optional[float] - payout_ratio: Optional[float] - sales_general_and_administrative_to_revenue: Optional[float] - research_and_development_to_revenue: Optional[float] - intangibles_to_total_assets: Optional[float] - capex_to_operating_cash_flow: Optional[float] - capex_to_revenue: Optional[float] - capex_to_depreciation: Optional[float] - stock_based_compensation_to_revenue: Optional[float] - graham_number: Optional[float] - roic: Optional[float] - return_on_tangible_assets: Optional[float] - graham_net_net: Optional[float] - working_capital: Optional[float] - tangible_asset_value: Optional[float] - net_current_asset_value: Optional[float] - invested_capital: Optional[float] - average_receivables: Optional[float] - average_payables: Optional[float] - average_inventory: Optional[float] - days_sales_outstanding: Optional[float] - days_payables_outstanding: Optional[float] - days_of_inventory_on_hand: Optional[float] - receivables_turnover: Optional[float] - payables_turnover: Optional[float] - inventory_turnover: Optional[float] - roe: Optional[float] - capex_per_share: Optional[float] diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/major_indices_constituents.py b/openbb_sdk/sdk/provider/openbb_provider/models/major_indices_constituents.py deleted file mode 100644 index 9ea3d709112..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/major_indices_constituents.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Major Indices Constituents data model.""" - - -from datetime import date -from typing import Literal, Optional, Union - -from pydantic import Field - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.metadata import DESCRIPTIONS - - -class MajorIndicesConstituentsQueryParams(QueryParams): - """Major Indices Constituents query. - - Parameter - --------- - index : Literal['nasdaq', 'sp500', 'dowjones'] - The index for which we want to fetch the constituents. Default is 'dowjones'. - """ - - index: Literal["nasdaq", "sp500", "dowjones"] = Field( - description=DESCRIPTIONS.get("index", ""), - default="dowjones", - ) - - -class MajorIndicesConstituentsData(Data): - """Major Indices Constituents price data. - - Returns - ------- - symbol : str - The symbol of the constituent company in the index. - name : str - The name of the constituent company in the index. - sector : str - The sector the constituent company in the index belongs to. - subSector : str - The sub-sector the constituent company in the index belongs to. - headQuarter : str - The location of the headquarter of the constituent company in the index. - dateFirstAdded : date - The date the constituent company was added to the index. - cik : int - The Central Index Key of the constituent company in the index. - founded : date - The founding year of the constituent company in the index. - """ - - name: str - sector: str - subSector: Optional[str] = Field(alias="sub_sector") - headQuarter: Optional[str] = Field(alias="headquarter") - dateFirstAdded: Optional[Union[date, str]] = Field(alias="date_first_added") - cik: int - founded: Union[date, str] diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/major_indices_eod.py b/openbb_sdk/sdk/provider/openbb_provider/models/major_indices_eod.py deleted file mode 100644 index 4f29a63edc6..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/major_indices_eod.py +++ /dev/null @@ -1,60 +0,0 @@ -"""Major indices aggregate end of day price data model.""" - - -from datetime import date, datetime -from typing import Optional - -from pydantic import Field, PositiveFloat - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.metadata import DESCRIPTIONS -from openbb_provider.models.base import BaseSymbol - - -class MajorIndicesEODQueryParams(QueryParams, BaseSymbol): - """Major Indices end of day query. - - Parameter - --------- - symbol : str - The symbol of the company. - start_date : date - The start date of the stock data from which to retrieve the data. - end_date : Optional[date] - The end date of the stock data up to which to retrieve the data. - """ - - start_date: date = Field(description=DESCRIPTIONS.get("start_date", "")) - end_date: Optional[date] = Field( - description=DESCRIPTIONS.get("end_date", ""), default=date.today() - ) - - -class MajorIndicesEODData(Data): - """Major Indices end of day price data. - - Returns - ------- - date : datetime - The date of the stock. - open : PositiveFloat - The open price of the stock. - high : PositiveFloat - The high price of the stock. - low : PositiveFloat - The low price of the stock. - close : PositiveFloat - The close price of the stock. - adj_close : Optional[PositiveFloat] - The adjusted close price of the stock. - volume : Optional[PositiveFloat] - The volume of the stock. - """ - - date: datetime - open: PositiveFloat - high: PositiveFloat - low: PositiveFloat - close: PositiveFloat - adj_close: Optional[PositiveFloat] - volume: Optional[PositiveFloat] diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/major_indices_price.py b/openbb_sdk/sdk/provider/openbb_provider/models/major_indices_price.py deleted file mode 100644 index a5ad477cc56..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/major_indices_price.py +++ /dev/null @@ -1,43 +0,0 @@ -"""Major Indices Price Data Model.""" - - -from datetime import datetime - -from pydantic import PositiveFloat - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class MajorIndicesPriceQueryParams(QueryParams, BaseSymbol): - """Major indices price query. - - Parameter - --------- - symbol : str - The symbol of the index. - """ - - -class MajorIndicesPriceData(Data): - """Major Indices price data. - - Returns - ------- - open : PositiveFloat - The open price of the stock. - high : PositiveFloat - The high price of the stock. - low : PositiveFloat - The low price of the stock. - close : PositiveFloat - The close price of the stock. - date : datetime - The date of the stock. - """ - - open: PositiveFloat - high: PositiveFloat - low: PositiveFloat - close: PositiveFloat - date: datetime diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/price_target.py b/openbb_sdk/sdk/provider/openbb_provider/models/price_target.py deleted file mode 100644 index f8d268752af..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/price_target.py +++ /dev/null @@ -1,60 +0,0 @@ -"""Price target data model.""" - - -from datetime import datetime -from typing import Optional - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class PriceTargetQueryParams(QueryParams, BaseSymbol): - """Price Target query. - - Parameter - --------- - symbol : str - The symbol of the company. - """ - - -class PriceTargetData(Data, BaseSymbol): - """Price target data. - - Returns - ------- - symbol : str - The symbol of the asset. - published_date : datetime - The published date of the price target. - news_url : str - The news URL of the price target. - news_title : str - The news title of the price target. - analyst_name : str - The analyst name of the price target. - price_target : float - The price target of the price target. - adj_price_target : float - The adjusted price target of the price target. - price_when_posted : float - The price when posted of the price target. - news_publisher : str - The news publisher of the price target. - news_base_url : str - The news base URL of the price target. - analyst_company : str - The analyst company of the price target. - """ - - symbol: str - published_date: datetime - news_url: str - news_title: Optional[str] - analyst_name: Optional[str] - price_target: float - adj_price_target: float - price_when_posted: float - news_publisher: str - news_base_url: str - analyst_company: str diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/price_target_consensus.py b/openbb_sdk/sdk/provider/openbb_provider/models/price_target_consensus.py deleted file mode 100644 index 89046b5a86b..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/price_target_consensus.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Price target consensus data model.""" - - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class PriceTargetConsensusQueryParams(QueryParams, BaseSymbol): - """Price Target Consensus query. - - Parameter - --------- - symbol : str - The symbol of the company. - """ - - -class PriceTargetConsensusData(Data): - """Price target consensus data. - - Returns - ------- - symbol : str - The symbol of the asset. - target_high : float - The high target of the price target consensus. - target_low : float - The low target of the price target consensus. - target_consensus : float - The consensus target of the price target consensus. - target_median : float - The median target of the price target consensus. - """ - - symbol: str - target_high: float - target_low: float - target_consensus: float - target_median: float diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/revenue_business_line.py b/openbb_sdk/sdk/provider/openbb_provider/models/revenue_business_line.py deleted file mode 100644 index 19477b968e2..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/revenue_business_line.py +++ /dev/null @@ -1,48 +0,0 @@ -"""Revenue by business line data model.""" - - -from datetime import date -from typing import Dict, Literal - -from pydantic import Field - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.metadata import DESCRIPTIONS -from openbb_provider.models.base import BaseSymbol - - -class RevenueBusinessLineQueryParams(QueryParams, BaseSymbol): - """Revenue business line query. - - - Parameter - --------- - symbol : str - The symbol of the company. - period : Literal["quarterly", "annually"] - The period of the income statement. - structure : Literal["hierarchical", "flat"] - The structure of the revenue business line. Should always be flat. - """ - - period: Literal["quarterly", "annually"] = Field( - default="quarterly", description=DESCRIPTIONS.get("period", "") - ) - structure: Literal["hierarchical", "flat"] = Field( - default="flat" - ) # should always be flat - - -class RevenueBusinessLineData(Data): - """Revenue by business line data. - - Returns - ------- - date : date - The date of the revenue. - data_and_service : Dict[str, int] - The data and service of the revenue. - """ - - date: date - data_and_service: Dict[str, int] diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/revenue_geographic.py b/openbb_sdk/sdk/provider/openbb_provider/models/revenue_geographic.py deleted file mode 100644 index 1e3f3733949..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/revenue_geographic.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Revenue by geographic segments data model.""" - - -from datetime import date -from typing import Literal, Optional - -from pydantic import Field - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.metadata import DESCRIPTIONS -from openbb_provider.models.base import BaseSymbol - - -class RevenueGeographicQueryParams(QueryParams, BaseSymbol): - """Revenue geographic query. - - Parameter - --------- - symbol : str - The symbol of the company. - period : Literal["quarterly", "annually"] - The period of the income statement. - structure : Literal["hierarchical", "flat"] - The structure of the revenue geographic. Should always be flat. - """ - - period: Literal["quarterly", "annually"] = Field( - default="quarterly", description=DESCRIPTIONS.get("period", "") - ) - structure: Literal["hierarchical", "flat"] = Field( - default="flat" - ) # should always be flat - - -class RevenueGeographicData(Data): - """Revenue by geographic segments data. - - Returns - ------- - date : date - The date of the revenue. - americas : Optional[int] - The revenue of the America segment. - europe : Optional[int] - The revenue of the Europe segment. - greater_china : Optional[int] - The revenue of the Greater China segment. - japan : Optional[int] - The revenue of the Japan segment. - rest_of_asia_pacific : Optional[int] - The revenue of the Rest of Asia Pacific segment. - """ - - date: date - americas: Optional[int] - europe: Optional[int] - greater_china: Optional[int] - japan: Optional[int] - rest_of_asia_pacific: Optional[int] diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/risk_premium.py b/openbb_sdk/sdk/provider/openbb_provider/models/risk_premium.py deleted file mode 100644 index 2b5692bcead..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/risk_premium.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Risk Premium data model.""" - - -from typing import Optional - -from pydantic import Field, NonNegativeFloat, PositiveFloat - -from openbb_provider.abstract.data import Data, QueryParams - - -class RiskPremiumQueryParams(QueryParams): - """Risk Premium query.""" - - -class RiskPremiumData(Data): - """Risk Premium data. - - Returns - ------- - country : str - continent : str - totalEquityRiskPremium : PositiveFloat - countryRiskPremium : PositiveFloat - """ - - country: str - continent: Optional[str] - totalEquityRiskPremium: PositiveFloat = Field(alias="total_equity_risk_premium") - countryRiskPremium: NonNegativeFloat = Field(alias="country_risk_premium") diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/sec_filings.py b/openbb_sdk/sdk/provider/openbb_provider/models/sec_filings.py deleted file mode 100644 index 1634bc21d74..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/sec_filings.py +++ /dev/null @@ -1,58 +0,0 @@ -"""SEC filings data model.""" - - -from datetime import date as dateType -from typing import Optional - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class SECFilingsQueryParams(QueryParams, BaseSymbol): - """SEC Filings query model. - Parameter - --------- - symbol : str - The symbol of the company. - type : str - The type of the SEC filing form. (full list: https://www.sec.gov/forms) - page : int - The page of the results. - limit : int - The limit of the results. - """ - - symbol: str - type: Optional[str] = None - page: Optional[int] = 0 - limit: Optional[int] = None - - -class SECFilingsData(Data): - """SEC filings data. - - Returns - ------- - symbol : str - The symbol of the stock. - filling_date : date - The filling date of the SEC filing. - accepted_date : date - The accepted date of the SEC filing. - cik : str - The CIK of the SEC filing. - type : str - The type of the SEC filing. - link : str - The link of the SEC filing. - final_link : str - The final link of the SEC filing. - """ - - symbol: str - filling_date: dateType - accepted_date: dateType - cik: Optional[str] - type: str - link: str - final_link: str diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/share_statistics.py b/openbb_sdk/sdk/provider/openbb_provider/models/share_statistics.py deleted file mode 100644 index 9b430179f63..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/share_statistics.py +++ /dev/null @@ -1,44 +0,0 @@ -"""Share Statistics Data Model.""" - - -from datetime import date as dateType - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class ShareStatisticsQueryParams(QueryParams, BaseSymbol): - """Share Statistics query. - - Parameter - --------- - symbol : str - The symbol of the company. - """ - - -class ShareStatisticsData(Data): - """Return Share Statistics Data. - - Returns - ------- - symbol : str - The symbol of the company. - date : dateType - The date of the share statistics. - free_float : float - The free float of the company. - float_shares : float - The float shares of the company. - outstanding_shares : float - The outstanding shares of the company. - source : str - The source of the data. - """ - - symbol: str - date: dateType - free_float: float - float_shares: float - outstanding_shares: float - source: str diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/stock_eod.py b/openbb_sdk/sdk/provider/openbb_provider/models/stock_eod.py deleted file mode 100644 index 4bfb3ec837a..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/stock_eod.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Stock aggregate end of day price data model.""" - - -from datetime import date, datetime -from typing import Optional - -from pydantic import Field, PositiveFloat - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.metadata import DESCRIPTIONS -from openbb_provider.models.base import BaseSymbol - - -class StockEODQueryParams(QueryParams, BaseSymbol): - """Stock end of day query. - Parameter - --------- - symbol : str - The symbol of the company. - start_date : Optional[date] - The start date of the stock data from which to retrieve the data. - end_date : Optional[date] - The end date of the stock data up to which to retrieve the data. - """ - - start_date: Optional[date] = Field( - description=DESCRIPTIONS.get("start_date", ""), - default=None, - ) - end_date: Optional[date] = Field( - description=DESCRIPTIONS.get("end_date", ""), default=None - ) - - -class StockEODData(Data): - """Stock end of day price data. - - Returns - ------- - date : datetime - The date of the stock. - open : PositiveFloat - The open price of the stock. - high : PositiveFloat - The high price of the stock. - low : PositiveFloat - The low price of the stock. - close : PositiveFloat - The close price of the stock. - adj_close : Optional[PositiveFloat] - The adjusted close price of the stock. - volume : PositiveFloat - The volume of the stock. - """ - - date: datetime - open: PositiveFloat - high: PositiveFloat - low: PositiveFloat - close: PositiveFloat - volume: PositiveFloat diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/stock_insider_trading.py b/openbb_sdk/sdk/provider/openbb_provider/models/stock_insider_trading.py deleted file mode 100644 index f433e293c3d..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/stock_insider_trading.py +++ /dev/null @@ -1,111 +0,0 @@ -"""Stock insider trading data model.""" - - -from datetime import date, datetime -from enum import Enum -from typing import List, Optional - -from openbb_provider.abstract.data import Data, QueryParams - - -class TransactionTypes(str, Enum): - Award = "A-Award" - Conversion = "C-Conversion" - Return = "D-Return" - ExpireShort = "E-ExpireShort" - InKind = "F-InKind" - Gift = "G-Gift" - ExpireLong = "H-ExpireLong" - Discretionary = "I-Discretionary" - Other = "J-Other" - Small = "L-Small" - Exempt = "M-Exempt" - OutOfTheMoney = "O-OutOfTheMoney" - Purchase = "P-Purchase" - Sale = "S-Sale" - Tender = "U-Tender" - Will = "W-Will" - InTheMoney = "X-InTheMoney" - Trust = "Z-Trust" - - -class StockInsiderTradingQueryParams(QueryParams): - """Stock Insider Trading query. - - - Parameter - --------- - transactionType : List[TransactionTypes] - The type of the transaction. Possible values are: - A-Award, C-Conversion, D-Return, E-ExpireShort, F-InKind, G-Gift, H-ExpireLong - I-Discretionary, J-Other, L-Small, M-Exempt, O-OutOfTheMoneym P-Purchase - S-Sale, U-Tender, W-Will, X-InTheMoney, Z-Trust - symbol : str] - The symbol of the company. - reportingCik : int] - The CIK of the reporting owner. - companyCik : int] - The CIK of the company owner. - page: int - The page number to get - """ - - transactionType: List[TransactionTypes] = [] - symbol: Optional[str] - reportingCik: Optional[int] - companyCik: Optional[int] - page: int = 0 - - -class StockInsiderTradingData(Data): - """Stock insider trading data. - - Returns - ------- - symbol : str - The symbol of the asset. - filingDate : datetime - The filing date of the stock insider trading. - transactionDate : date - The transaction date of the stock insider trading. - reportingCik : int - The reporting CIK of the stock insider trading. - transactionType : str - The transaction type of the stock insider trading. - securitiesOwned : int - The securities owned of the stock insider trading. - companyCik : int - The company CIK of the stock insider trading. - reportingName : str - The reporting name of the stock insider trading. - typeOfOwner : str - The type of owner of the stock insider trading. - acquistionOrDisposition : str - The acquistion or disposition of the stock insider trading. - formType : str - The form type of the stock insider trading. - securitiesTransacted : float - The securities transacted of the stock insider trading. - price : float - The price of the stock insider trading. - securityName : str - The security name of the stock insider trading. - link : str - The link of the stock insider trading. - """ - - symbol: str - filing_date: datetime - transaction_date: date - reporting_cik: int - transaction_type: str - securities_owned: int - company_cik: int - reporting_name: str - type_of_owner: str - acquistion_or_disposition: str - form_type: str - securities_transacted: float - price: float - security_name: str - link: str diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/stock_multiples.py b/openbb_sdk/sdk/provider/openbb_provider/models/stock_multiples.py deleted file mode 100644 index 5618640478b..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/stock_multiples.py +++ /dev/null @@ -1,200 +0,0 @@ -"""Stock Multiples Data Model.""" - -from typing import Optional - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class StockMultiplesQueryParams(QueryParams, BaseSymbol): - """Stock Multiples query model. - - Parameter - --------- - symbol : str - The symbol of the company. - limit : Optional[int] - The limit of the key metrics ttm to be returned. - """ - - limit: Optional[int] = None - - -class StockMultiplesData(Data): - """Stock Multiples Data. - - Returns - ------- - revenue_per_share_ttm: Optional[float] - The revenue per share of the stock calculated as trailing twelve months. - net_income_per_share_ttm: Optional[float] - The net income per share of the stock calculated as trailing twelve months. - operating_cash_flow_per_share_ttm: Optional[float] - The operating cash flow per share of the stock calculated as trailing twelve months. - free_cash_flow_per_share_ttm: Optional[float] - The free cash flow per share of the stock calculated as trailing twelve months. - cash_per_share_ttm: Optional[float] - The cash per share of the stock calculated as trailing twelve months. - book_value_per_share_ttm: Optional[float] - The book value per share of the stock calculated as trailing twelve months. - tangible_book_value_per_share_ttm: Optional[float] - The tangible book value per share of the stock calculated as trailing twelve months. - shareholders_equity_per_share_ttm: Optional[float] - The shareholders equity per share of the stock calculated as trailing twelve months. - interest_debt_per_share_ttm: Optional[float] - The interest debt per share of the stock calculated as trailing twelve months. - market_cap_ttm: Optional[float] - The market cap of the stock calculated as trailing twelve months. - enterprise_value_ttm: Optional[float] - The enterprise value of the stock calculated as trailing twelve months. - pe_ratio_ttm: Optional[float] - The PE ratio of the stock calculated as trailing twelve months. - price_to_sales_ratio_ttm: Optional[float] - The price to sales ratio of the stock calculated as trailing twelve months. - pocf_ratio_ttm: Optional[float] - The POCF ratio of the stock calculated as trailing twelve months. - pfcf_ratio_ttm: Optional[float] - The PFCF ratio of the stock calculated as trailing twelve months. - pb_ratio_ttm: Optional[float] - The PB ratio of the stock calculated as trailing twelve months. - ptb_ratio_ttm: Optional[float] - The PTB ratio of the stock calculated as trailing twelve months. - ev_to_sales_ttm: Optional[float] - The EV to sales of the stock calculated as trailing twelve months. - enterprise_value_over_ebitda_ttm: Optional[float] - The enterprise value over EBITDA of the stock calculated as trailing twelve months. - ev_to_operating_cash_flow_ttm: Optional[float] - The EV to operating cash flow of the stock calculated as trailing twelve months. - ev_to_free_cash_flow_ttm: Optional[float] - The EV to free cash flow of the stock calculated as trailing twelve months. - earnings_yield_ttm: Optional[float] - The earnings yield of the stock calculated as trailing twelve months. - free_cash_flow_yield_ttm: Optional[float] - The free cash flow yield of the stock calculated as trailing twelve months. - debt_to_equity_ttm: Optional[float] - The debt to equity of the stock calculated as trailing twelve months. - debt_to_assets_ttm: Optional[float] - The debt to assets of the stock calculated as trailing twelve months. - net_debt_to_ebitda_ttm: Optional[float] - The net debt to EBITDA of the stock calculated as trailing twelve months. - current_ratio_ttm: Optional[float] - The current ratio of the stock calculated as trailing twelve months. - interest_coverage_ttm: Optional[float] - The interest coverage of the stock calculated as trailing twelve months. - income_quality_ttm: Optional[float] - The income quality of the stock calculated as trailing twelve months. - dividend_yield_ttm: Optional[float] - The dividend yield of the stock calculated as trailing twelve months. - payout_ratio_ttm: Optional[float] - The payout ratio of the stock calculated as trailing twelve months. - sales_general_and_administrative_to_revenue_ttm: Optional[float] - The sales general and administrative to revenue of the stock calculated as trailing twelve months. - research_and_development_to_revenue_ttm: Optional[float] - The research and development to revenue of the stock calculated as trailing twelve months. - intangibles_to_total_assets_ttm: Optional[float] - The intangibles to total assets of the stock calculated as trailing twelve months. - capex_to_operating_cash_flow_ttm: Optional[float] - The capex to operating cash flow of the stock calculated as trailing twelve months. - capex_to_revenue_ttm: Optional[float] - The capex to revenue of the stock calculated as trailing twelve months. - capex_to_depreciation_ttm: Optional[float] - The capex to depreciation of the stock calculated as trailing twelve months. - stock_based_compensation_to_revenue_ttm: Optional[float] - The stock based compensation to revenue of the stock calculated as trailing twelve months. - graham_number_ttm: Optional[float] - The graham number of the stock calculated as trailing twelve months. - roic_ttm: Optional[float] - The ROIC of the stock calculated as trailing twelve months. - return_on_tangible_assets_ttm: Optional[float] - The return on tangible assets of the stock calculated as trailing twelve months. - graham_net_net_ttm: Optional[float] - The graham net net of the stock calculated as trailing twelve months. - working_capital_ttm: Optional[float] - The working capital of the stock calculated as trailing twelve months. - tangible_asset_value_ttm: Optional[float] - The tangible asset value of the stock calculated as trailing twelve months. - net_current_asset_value_ttm: Optional[float] - The net current asset value of the stock calculated as trailing twelve months. - invested_capital_ttm: Optional[float] - The invested capital of the stock calculated as trailing twelve months. - average_receivables_ttm: Optional[float] - The average receivables of the stock calculated as trailing twelve months. - average_payables_ttm: Optional[float] - The average payables of the stock calculated as trailing twelve months. - average_inventory_ttm: Optional[float] - The average inventory of the stock calculated as trailing twelve months. - days_sales_outstanding_ttm: Optional[float] - The days sales outstanding of the stock calculated as trailing twelve months. - days_payables_outstanding_ttm: Optional[float] - The days payables outstanding of the stock calculated as trailing twelve months. - days_of_inventory_on_hand_ttm: Optional[float] - The days of inventory on hand of the stock calculated as trailing twelve months. - receivables_turnover_ttm: Optional[float] - The receivables turnover of the stock calculated as trailing twelve months. - payables_turnover_ttm: Optional[float] - The payables turnover of the stock calculated as trailing twelve months. - inventory_turnover_ttm: Optional[float] - The inventory turnover of the stock calculated as trailing twelve months. - roe_ttm: Optional[float] - The ROE of the stock calculated as trailing twelve months. - capex_per_share_ttm: Optional[float] - The capex per share of the stock calculated as trailing twelve months. - """ - - revenue_per_share_ttm: Optional[float] - net_income_per_share_ttm: Optional[float] - operating_cash_flow_per_share_ttm: Optional[float] - free_cash_flow_per_share_ttm: Optional[float] - cash_per_share_ttm: Optional[float] - book_value_per_share_ttm: Optional[float] - tangible_book_value_per_share_ttm: Optional[float] - shareholders_equity_per_share_ttm: Optional[float] - interest_debt_per_share_ttm: Optional[float] - market_cap_ttm: Optional[float] - enterprise_value_ttm: Optional[float] - pe_ratio_ttm: Optional[float] - price_to_sales_ratio_ttm: Optional[float] - pocf_ratio_ttm: Optional[float] - pfcf_ratio_ttm: Optional[float] - pb_ratio_ttm: Optional[float] - ptb_ratio_ttm: Optional[float] - ev_to_sales_ttm: Optional[float] - enterprise_value_over_ebitda_ttm: Optional[float] - ev_to_operating_cash_flow_ttm: Optional[float] - ev_to_free_cash_flow_ttm: Optional[float] - earnings_yield_ttm: Optional[float] - free_cash_flow_yield_ttm: Optional[float] - debt_to_equity_ttm: Optional[float] - debt_to_assets_ttm: Optional[float] - net_debt_to_ebitda_ttm: Optional[float] - current_ratio_ttm: Optional[float] - interest_coverage_ttm: Optional[float] - income_quality_ttm: Optional[float] - dividend_yield_ttm: Optional[float] - payout_ratio_ttm: Optional[float] - sales_general_and_administrative_to_revenue_ttm: Optional[float] - research_and_development_to_revenue_ttm: Optional[float] - intangibles_to_total_assets_ttm: Optional[float] - capex_to_operating_cash_flow_ttm: Optional[float] - capex_to_revenue_ttm: Optional[float] - capex_to_depreciation_ttm: Optional[float] - stock_based_compensation_to_revenue_ttm: Optional[float] - graham_number_ttm: Optional[float] - roic_ttm: Optional[float] - return_on_tangible_assets_ttm: Optional[float] - graham_net_net_ttm: Optional[float] - working_capital_ttm: Optional[float] - tangible_asset_value_ttm: Optional[float] - net_current_asset_value_ttm: Optional[float] - invested_capital_ttm: Optional[float] - average_receivables_ttm: Optional[float] - average_payables_ttm: Optional[float] - average_inventory_ttm: Optional[float] - days_sales_outstanding_ttm: Optional[float] - days_payables_outstanding_ttm: Optional[float] - days_of_inventory_on_hand_ttm: Optional[float] - receivables_turnover_ttm: Optional[float] - payables_turnover_ttm: Optional[float] - inventory_turnover_ttm: Optional[float] - roe_ttm: Optional[float] - capex_per_share_ttm: Optional[float] diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/stock_news.py b/openbb_sdk/sdk/provider/openbb_provider/models/stock_news.py deleted file mode 100644 index dd97e10f751..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/stock_news.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Stock News Data Model.""" - - -from datetime import date -from typing import Optional - -from pydantic import Field, validator - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.metadata import DESCRIPTIONS - - -class StockNewsQueryParams(QueryParams): - """Stock news query. - - - Parameter - --------- - symbols : str - The symbols of the company. - page : int - The page of the stock news to be retrieved. - """ - - symbols: str = Field(min_length=1, description=DESCRIPTIONS.get("symbols", "")) - page: int = Field(default=0) - - @validator("symbols", pre=True) - def time_validate(cls, v: str): # pylint: disable=E0213 - return v.upper() - - -class StockNewsData(Data): - """Stock News data. - - Returns - ------- - date : date - The published date of the news. - title : str - The title of the news. - image : Optional[str] - The image URL of the news. - text : str - The text/body of the news. - url : str - The URL of the news. - """ - - date: date - title: str - image: Optional[str] - text: Optional[str] - url: str diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/stock_ownership.py b/openbb_sdk/sdk/provider/openbb_provider/models/stock_ownership.py deleted file mode 100644 index b80d4374516..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/stock_ownership.py +++ /dev/null @@ -1,189 +0,0 @@ -"""Stock owner data model.""" - - -from datetime import ( - date as dateType, - datetime, -) - -from pydantic import validator - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -def most_recent_quarter(base: dateType = dateType.today()) -> dateType: - """Get the most recent quarter date. - - Parameter - --------- - base : date - The date to get the most recent quarter. - - Returns - ------- - date : date - The most recent quarter date. - """ - - base = min(base, dateType.today()) # This prevents dates from being in the future - exacts = [(3, 31), (6, 30), (9, 30), (12, 31)] - for exact in exacts: - if base.month == exact[0] and base.day == exact[1]: - return base - if base.month < 4: - return dateType(base.year - 1, 12, 31) - if base.month < 7: - return dateType(base.year, 3, 31) - if base.month < 10: - return dateType(base.year, 6, 30) - return dateType(base.year, 9, 30) - - -class StockOwnershipQueryParams(QueryParams, BaseSymbol): - """Stock ownership query. - - - Parameter - --------- - symbol : string - The symbol of the company. - page : int - The page number to get - date : date - The CIK of the company owner. - """ - - page: int = 0 - date: dateType = most_recent_quarter() - - @validator("date", pre=True) - def time_validate(cls, v: str): # pylint: disable=E0213 - base = datetime.strptime(v, "%Y-%m-%d").date() - return most_recent_quarter(base) - - -class StockOwnershipData(Data): - """Stock ownership data. - - Returns - ------- - date : date - The date of the stock ownership. - cik : int - The cik of the stock ownership. - filingDate : date - The filing date of the stock ownership. - investorName : str - The investor name of the stock ownership. - symbol : str - The symbol of the stock ownership. - securityName : str - The security name of the stock ownership. - typeOfSecurity : str - The type of security of the stock ownership. - securityCusip : str - The security cusip of the stock ownership. - sharesType : str - The shares type of the stock ownership. - putCallShare : str - The put call share of the stock ownership. - investmentDiscretion : str - The investment discretion of the stock ownership. - industryTitle : str - The industry title of the stock ownership. - weight : float - The weight of the stock ownership. - lastWeight : float - The last weight of the stock ownership. - changeInWeight : float - The change in weight of the stock ownership. - changeInWeightPercentage : float - The change in weight percentage of the stock ownership. - marketValue : int - The market value of the stock ownership. - lastMarketValue : int - The last market value of the stock ownership. - changeInMarketValue : int - The change in market value of the stock ownership. - changeInMarketValuePercentage : float - The change in market value percentage of the stock ownership. - sharesNumber : int - The shares number of the stock ownership. - lastSharesNumber : int - The last shares number of the stock ownership. - changeInSharesNumber : float - The change in shares number of the stock ownership. - changeInSharesNumberPercentage : float - The change in shares number percentage of the stock ownership. - quarterEndPrice : float - The quarter end price of the stock ownership. - avgPricePaid : float - The average price paid of the stock ownership. - isNew : bool - Is the stock ownership new. - isSoldOut : bool - Is the stock ownership sold out. - ownership : float - How much is the ownership. - lastOwnership : float - The last ownership amount. - changeInOwnership : float - The change in ownership amount. - changeInOwnershipPercentage : float - The change in ownership percentage. - holdingPeriod : int - The holding period of the stock ownership. - firstAdded : date - The first added date of the stock ownership. - performance : float - The performance of the stock ownership. - performancePercentage : float - The performance percentage of the stock ownership. - lastPerformance : float - The last performance of the stock ownership. - changeInPerformance : float - The change in performance of the stock ownership. - isCountedForPerformance : bool - Is the stock ownership counted for performance. - """ - - date: dateType - cik: int - filingDate: dateType - investorName: str - symbol: str - securityName: str - typeOfSecurity: str - securityCusip: str - sharesType: str - putCallShare: str - investmentDiscretion: str - industryTitle: str - weight: float - lastWeight: float - changeInWeight: float - changeInWeightPercentage: float - marketValue: int - lastMarketValue: int - changeInMarketValue: int - changeInMarketValuePercentage: float - sharesNumber: int - lastSharesNumber: int - changeInSharesNumber: float - changeInSharesNumberPercentage: float - quarterEndPrice: float - avgPricePaid: float - isNew: bool - isSoldOut: bool - ownership: float - lastOwnership: float - changeInOwnership: float - changeInOwnershipPercentage: float - holdingPeriod: int - firstAdded: dateType - performance: float - performancePercentage: float - lastPerformance: float - changeInPerformance: float - isCountedForPerformance: bool diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/stock_peers.py b/openbb_sdk/sdk/provider/openbb_provider/models/stock_peers.py deleted file mode 100644 index 862cf668252..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/stock_peers.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Stock Peers data model.""" - -from typing import List, Optional - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol - - -class StockPeersQueryParams(QueryParams, BaseSymbol): - """Stock Peers query model. - - Parameter - --------- - symbol: str - The symbol of the company. - """ - - -class StockPeersData(Data): - """Stock Peers data. - - Returns - ------- - symbol : str - The symbol of the company to retrieve the stock peers of. - peers_list : List[str] - A list of stock peers based on sector, exchange and market cap - """ - - symbol: str - peers_list: Optional[List[str]] diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/stock_price.py b/openbb_sdk/sdk/provider/openbb_provider/models/stock_price.py deleted file mode 100644 index 71616ed75b3..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/stock_price.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Stock Price Data Model.""" - - -from datetime import date, datetime -from typing import Optional - -from pydantic import Field, PositiveFloat - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.metadata import DESCRIPTIONS -from openbb_provider.models.base import BaseSymbol - - -class StockPriceQueryParams(QueryParams, BaseSymbol): - """Stock price query. - - Parameter - --------- - symbol : str - The symbol of the company. - start_date : date - The start date of the query. - end_date : Optional[date] - The end date of the query. - """ - - # These fields only work with Polygon - start_date: date = Field(description=DESCRIPTIONS.get("start_date", "")) - end_date: Optional[date] = Field( - description=DESCRIPTIONS.get("end_date", ""), default=date.today() - ) - - -class StockPriceData(Data): - """Stock price data. - - Returns - ------- - open : PositiveFloat - The open price of the stock. - high : PositiveFloat - The high price of the stock. - low : PositiveFloat - The low price of the stock. - close : PositiveFloat - The close price of the stock. - date : datetime - The date of the stock. - """ - - open: PositiveFloat - high: PositiveFloat - low: PositiveFloat - close: PositiveFloat - date: datetime diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/stock_splits.py b/openbb_sdk/sdk/provider/openbb_provider/models/stock_splits.py deleted file mode 100644 index 369903b9c36..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/stock_splits.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Stock Split Calendar data model.""" - - -from datetime import date as dateType -from typing import Optional - -from pydantic import Field - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.metadata import DESCRIPTIONS - - -class StockSplitCalendarQueryParams(QueryParams): - """Stock Split Calendar query model. - - Parameter - --------- - start_date : date - The start date of the stock splits from which to retrieve the data. - end_date : date - The end date of the stock splits up to which to retrieve the data. - """ - - start_date: Optional[dateType] = Field( - description=DESCRIPTIONS.get("start_date", ""), default=None - ) - end_date: Optional[dateType] = Field( - description=DESCRIPTIONS.get("end_date", ""), default=None - ) - - -class StockSplitCalendarData(Data): - """Stock Split Calendar data. - - Returns - ------- - date : date - The date of the stock splits. - label : str - The label of the stock splits. - symbol : str - The symbol of the company. - numerator : float - The numerator of the stock splits. - denominator : float - The denominator of the stock splits. - """ - - date: dateType - label: str - symbol: str - numerator: float - denominator: float diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/treasury_rates.py b/openbb_sdk/sdk/provider/openbb_provider/models/treasury_rates.py deleted file mode 100644 index 32ac4532b9f..00000000000 --- a/openbb_sdk/sdk/provider/openbb_provider/models/treasury_rates.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Treasury Rates Data Model.""" - - -from datetime import ( - date as dateType, - datetime, -) -from typing import Optional - -from pydantic import Field - -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.metadata import DESCRIPTIONS - - -class TreasuryRatesQueryParams(QueryParams): - """Treasury Rates query. - - Parameter - --------- - start_date : Optional[str] - Start date of the data, default is None. - end_date : Optional[str] - End date of the data, default is today. - """ - - start_date: Optional[str] = Field( - default=None, description=DESCRIPTIONS["start_date"] - ) - end_date: Optional[str] = Field( - default=datetime.today().strftime("%Y-%m-%d"), - description=DESCRIPTIONS["end_date"], - ) - - -class TreasuryRatesData(Data): - """Return Treasury Rates Data. - - Returns - ------- - date : dateType - The date of the treasury rates. - month_1 : float - The 1 month treasury rate. - month_2 : float - The 2 month treasury rate. - month_3 : float - The 3 month treasury rate. - month_6 : float - The 6 month treasury rate. - year_1 : float - The 1 year treasury rate. - year_2 : float - The 2 year treasury rate. - year_3 : float - The 3 year treasury rate. - year_5 : float - The 5 year treasury rate. - year_7 : float - The 7 year treasury rate. - year_10 : float - The 10 year treasury rate. - year_20 : float - The 20 year treasury rate. - year_30 : float - The 30 year treasury rate. - """ - - date: dateType - month_1: float - month_2: float - month_3: float - month_6: float - year_1: float - year_2: float - year_3: float - year_5: float - year_7: float - year_10: float - year_20: float - year_30: float diff --git a/openbb_sdk/sdk/provider/openbb_provider/query_executor.py b/openbb_sdk/sdk/provider/openbb_provider/query_executor.py new file mode 100644 index 00000000000..6a1cf6352e6 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/query_executor.py @@ -0,0 +1,81 @@ +from typing import Any, Dict, Optional, Type + +from openbb_provider.abstract.fetcher import Fetcher, GenericDataType +from openbb_provider.abstract.provider import Provider +from openbb_provider.registry import Registry, RegistryLoader + + +class ProviderError(Exception): + pass + + +class QueryExecutor: + """Class to execute queries from providers.""" + + def __init__(self, registry: Optional[Registry] = None) -> None: + self.registry = registry or RegistryLoader.from_extensions() + + def get_provider(self, provider_name: str) -> Provider: + """Get a provider from the registry.""" + name = provider_name.lower() + if name not in self.registry.providers: + raise ProviderError( + f"Provider '{name}' not found in the registry." + f"Available providers: {list(self.registry.providers.keys())}" + ) + return self.registry.providers[name] + + def get_fetcher(self, provider: Provider, model_name: str) -> Type[Fetcher]: + """Get a fetcher from a provider.""" + if model_name not in provider.fetcher_dict: + raise ProviderError( + f"Fetcher not found for model '{model_name}' in provider '{provider.name}'." + ) + return provider.fetcher_dict[model_name] + + @staticmethod + def verify_credentials(provider: Provider, credentials: Optional[Dict[str, str]]): + """Verify credentials to match provider requirements.""" + if provider.required_credentials is not None: + if credentials is None: + credentials = {} + for c in provider.required_credentials: + credential_value = credentials.get(c) + if c not in credentials or credential_value is None: + raise ProviderError(f"Missing credential '{c}'.") + + def execute( + self, + provider_name: str, + model_name: str, + params: Dict[str, Any], + credentials: Optional[Dict[str, str]] = None, + **kwargs: Any, + ) -> GenericDataType: + """Execute query. + + Parameters + ---------- + provider_name : str + Name of the provider, for example: "fmp". + model_name : str + Name of the model, for example: "StockEOD". + params : Dict[str, Any] + Query parameters, for example: {"symbol": "AAPL"} + credentials : Optional[Dict[str, str]], optional + Credentials for the provider, by default None + For example, {"fmp_api_key": "1234"}. + + Returns + ------- + GenericDataType + Query result. + """ + provider = self.get_provider(provider_name) + self.verify_credentials(provider, credentials) + fetcher = self.get_fetcher(provider, model_name) + + try: + return fetcher.fetch_data(params, credentials, **kwargs) + except Exception as e: + raise ProviderError(e) from e diff --git a/openbb_sdk/sdk/provider/openbb_provider/registry.py b/openbb_sdk/sdk/provider/openbb_provider/registry.py index 81201996dbd..eccd6f6cee0 100644 --- a/openbb_sdk/sdk/provider/openbb_provider/registry.py +++ b/openbb_sdk/sdk/provider/openbb_provider/registry.py @@ -1,133 +1,38 @@ -"""ProviderRegistry class and Builder class.""" - - -from typing import Any, Dict, Optional +from typing import Dict import pkg_resources -from openbb_provider.abstract.data import QueryParams -from openbb_provider.abstract.fetcher import Fetcher from openbb_provider.abstract.provider import Provider -class ProviderError(Exception): +class Registry: + """Class to maintain registry of providers""" + + def __init__(self) -> None: + self._providers: Dict[str, Provider] = {} + + @property + def providers(self): + return self._providers + + def include_provider(self, provider: Provider) -> None: + self._providers[provider.name.lower()] = provider + + +class LoadingError(Exception): pass -class ProviderRegistry: - """A Provider Registry is the central executor for dynamically retrieving data.""" +class RegistryLoader: + @staticmethod + def from_extensions() -> Registry: + registry = Registry() + for entry_point in pkg_resources.iter_entry_points("openbb_provider_extension"): + try: + registry.include_provider( + provider=entry_point.load(), + ) + except Exception as e: + raise LoadingError(f"Invalid provider '{entry_point.name}': {e}") from e - def __init__(self) -> None: - self.providers: Dict[str, Provider] = {} - self.credentials: Dict[str, Dict[str, None]] = {} - - def get_provider(self, provider_name: str) -> Provider: - """Get a provider from the registry.""" - if not self.providers: - raise ValueError("No providers found, please confirm they are loaded.") - - try: - provider = self.providers[provider_name] - except KeyError as e: - raise ValueError( - f"{provider_name} is not part of the ProviderRegistry. " - f"Available providers are: {list(self.providers.keys())}" - ) from e - - return provider - - def filter_credentials( - self, - provider_name: str, - credentials: Optional[Dict[str, str]] = None, - ) -> Dict[str, str]: - required_credentials = self.credentials.get(provider_name) - - if required_credentials is None: - return {} - - result: Dict[str, str] = {} - if credentials is not None: - for k in required_credentials: - credential_value = credentials.get(k) - if k not in credentials or credential_value is None: - raise ProviderError( - f"Missing credential '{k}' for '{provider_name}'." - ) - - result[k] = credential_value - - return result - - def get_fetcher(self, provider: Provider, query_params: QueryParams) -> Fetcher: - """Get a fetcher from the provider.""" - fetch_dict = provider.fetcher_dict[ - "get_query_type" - ] # TODO: Check if we really need to pass this "get_query_type" - fetcher = fetch_dict.get(query_params.__name__) # type: ignore - if fetcher is None: - raise ProviderError( - f"This query is not supported by the '{provider.name}' provider. " - f"Please try another provider: '{list(self.providers.keys())}'" - ) - return fetcher - - def fetch( - self, - provider_name: str, - query_params: QueryParams, - extra_params: Optional[Dict] = None, - credentials: Optional[Dict[str, str]] = None, - ): - """Fetch data from a provider by using the OpenBB standard.""" - provider = self.get_provider(provider_name) - filtered_credentials = self.filter_credentials(provider_name, credentials) - Fetcher_ = self.get_fetcher(provider, query_params) - - try: - return Fetcher_.fetch_data(query_params, extra_params, filtered_credentials) - except Exception as e: - raise ProviderError(e) from e - - -def load_extensions(entry_point_group: str = "openbb_provider_extension") -> Any: - """Load extensions from entry points and their API keys from settings.""" - - # TODO: Figure actual type for dict values Union[Provider, Dict[str, Dict[str, None]]] - # Can we try to load the extensions directly into the ProviderRegistry? Let's try it. - - extensions_dict: Dict[str, Dict[str, Any]] = {} - extensions_dict["providers"] = {} - extensions_dict["credentials"] = {} - - entry_points = pkg_resources.iter_entry_points(entry_point_group) - for entry_point in entry_points: - e = entry_point.load() - - provider_name = str(e.name) - - extensions_dict["providers"][provider_name] = e - - required_credentials = getattr(e, "required_credentials", None) - if required_credentials: - extensions_dict["credentials"][provider_name] = {} - for credential in required_credentials: - if credential: - credential_name = provider_name.lower() + "_" + credential.lower() - extensions_dict["credentials"][provider_name][ - credential_name - ] = None - - return extensions_dict - - -def build_provider_registry(extensions_dict: Any) -> ProviderRegistry: - """Build a ProviderRegistry from a list of extensions and their API keys.""" - registry = ProviderRegistry() - registry.providers = extensions_dict["providers"] - registry.credentials = extensions_dict["credentials"] - return registry - - -extensions_dict__ = load_extensions("openbb_provider_extension") -provider_registry = build_provider_registry(extensions_dict=extensions_dict__) + return registry diff --git a/openbb_sdk/sdk/provider/openbb_provider/registry_map.py b/openbb_sdk/sdk/provider/openbb_provider/registry_map.py new file mode 100644 index 00000000000..330a719539d --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/registry_map.py @@ -0,0 +1,120 @@ +"""Provider registry map.""" + +import inspect +import os +from typing import Any, Dict, List, Literal, Optional + +from openbb_provider.abstract.fetcher import Fetcher +from openbb_provider.registry import Registry, RegistryLoader + +MapType = Dict[str, Dict[str, Dict[str, Dict[str, Any]]]] + + +class RegistryMap: + """Class to store information about providers in the registry.""" + + def __init__(self, registry: Optional[Registry] = None) -> None: + self._registry = registry or RegistryLoader.from_extensions() + self._required_credentials = self._get_required_credentials(self._registry) + self._available_providers = self._get_available_providers(self._registry) + self._map = self._get_map(self._registry) + self._models = self._get_models(self._map) + + @property + def registry(self) -> Registry: + """Get the registry.""" + return self._registry + + @property + def available_providers(self) -> List[str]: + """Get list of available providers.""" + return self._available_providers + + @property + def required_credentials(self) -> List[str]: + """Get list of required credentials.""" + return self._required_credentials + + @property + def map(self) -> MapType: + """Get provider registry map.""" + return self._map + + @property + def models(self) -> List[str]: + """Get available models.""" + return self._models + + def _get_required_credentials(self, registry: Registry) -> List[str]: + """Get list of required credentials.""" + cred_list = [] + for provider in registry.providers.values(): + for c in provider.required_credentials: + cred_list.append(c) + return cred_list + + def _get_available_providers(self, registry: Registry) -> List[str]: + """Get list of available providers.""" + return sorted(list(registry.providers.keys())) + + def _get_map(self, registry: Registry) -> MapType: + """Generate map for the provider package.""" + map_: MapType = {} + + for p in registry.providers: + for model_name, fetcher in registry.providers[p].fetcher_dict.items(): + f = fetcher() + standard_query, extra_query = self.extract_info(f, "query_params") + standard_data, extra_data = self.extract_info(f, "data") + + if model_name not in map_: + map_[model_name] = {} + map_[model_name]["openbb"] = { + "QueryParams": standard_query, + "Data": standard_data, + } + + map_[model_name][p] = { + "QueryParams": extra_query, + "Data": extra_data, + } + + return map_ + + def _get_models(self, map_: MapType) -> List[str]: + """Get available models.""" + return list(map_.keys()) + + @staticmethod + def extract_info(fetcher: Fetcher, type_: Literal["query_params", "data"]) -> tuple: + """Extract info (fields and docstring) from fetcher query params or data.""" + super_model = getattr(fetcher, f"provider_{type_}_type") + + skip_classes = {"object", "Representation", "BaseModel", "QueryParams", "Data"} + inheritance_list = [ + model for model in super_model.__mro__ if model.__name__ not in skip_classes + ] + + all_fields = {} + standard_info: Dict[str, Any] = {"fields": {}, "docstring": None} + found_standard = False + + for model in inheritance_list: + model_file_dir = os.path.dirname(inspect.getfile(model)) + model_name = os.path.basename(model_file_dir) + + if (model_name == "standard_models") or found_standard: + if not found_standard: + standard_info["docstring"] = model.__doc__ + found_standard = True + standard_info["fields"].update(model.__fields__) + else: + all_fields.update(model.__fields__) + + extra_info = {"fields": {}, "docstring": super_model.__doc__} + + for name, field in all_fields.items(): + if name not in standard_info["fields"]: + extra_info["fields"][name] = field + + return standard_info, extra_info diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/__init__.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/analyst_estimates.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/analyst_estimates.py new file mode 100644 index 00000000000..4944f498cd8 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/analyst_estimates.py @@ -0,0 +1,57 @@ +"""Analyst estimates data model.""" + + +from datetime import date as dateType +from typing import Literal + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS + + +class AnalystEstimatesQueryParams(QueryParams, BaseSymbol): + """Analyst Estimates query.""" + + period: Literal["quarterly", "annually"] = Field( + default="annually", description=QUERY_DESCRIPTIONS.get("period", "") + ) + limit: int = Field(default=30, description=QUERY_DESCRIPTIONS.get("limit", "")) + + +class AnalystEstimatesData(Data, BaseSymbol): + """Analyst estimates data.""" + + date: dateType = Field(description=QUERY_DESCRIPTIONS.get("date", "")) + estimated_revenue_low: int = Field(description="The estimated revenue low.") + estimated_revenue_high: int = Field(description="The estimated revenue high.") + estimated_revenue_avg: int = Field(description="The estimated revenue average.") + estimated_ebitda_low: int = Field(description="The estimated EBITDA low.") + estimated_ebitda_high: int = Field(description="The estimated EBITDA high.") + estimated_ebitda_avg: int = Field(description="The estimated EBITDA average.") + estimated_ebit_low: int = Field(description="The estimated EBIT low.") + estimated_ebit_high: int = Field(description="The estimated EBIT high.") + estimated_ebit_avg: int = Field(description="The estimated EBIT average.") + estimated_net_income_low: int = Field(description="The estimated net income low.") + estimated_net_income_high: int = Field(description="The estimated net income high.") + estimated_net_income_avg: int = Field( + description="The estimated net income average." + ) + estimated_sga_expense_low: int = Field(description="The estimated SGA expense low.") + estimated_sga_expense_high: int = Field( + description="The estimated SGA expense high." + ) + estimated_sga_expense_avg: int = Field( + description="The estimated SGA expense average." + ) + estimated_eps_avg: float = Field(description="The estimated EPS average.") + estimated_eps_high: float = Field(description="The estimated EPS high.") + estimated_eps_low: float = Field(description="The estimated EPS low.") + number_analyst_estimated_revenue: int = Field( + description="The number of analysts who estimated revenue." + ) + number_analysts_estimated_eps: int = Field( + description="The number of analysts who estimated EPS." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/available_indices.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/available_indices.py new file mode 100644 index 00000000000..d27bbbcb232 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/available_indices.py @@ -0,0 +1,30 @@ +"""Available Indices data model.""" + + +from typing import Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol + + +class AvailableIndicesQueryParams(QueryParams): + """Available Indices Query.""" + + +class AvailableIndicesData(Data, BaseSymbol): + """Available Indices Data. + + Returns the major indices from Dow Jones, Nasdaq and, S&P 500. + """ + + name: Optional[str] = Field(description="The name of the index.") + currency: Optional[str] = Field(description="The currency the index is traded in.") + stock_exchange: str = Field( + description="The stock exchange where the index is listed." + ) + exchange_short_name: str = Field( + description="The short name of the stock exchange where the index is listed." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/balance_sheet.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/balance_sheet.py new file mode 100644 index 00000000000..c5de1bc9813 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/balance_sheet.py @@ -0,0 +1,91 @@ +"""Balance Sheet Data Model.""" + + +from datetime import ( + date as dateType, + datetime, +) +from typing import Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.standard_models.base import FinancialStatementQueryParams + +# IMPORT THIRD PARTY + + +class BalanceSheetQueryParams(FinancialStatementQueryParams): + """Balance Sheet query.""" + + +class BalanceSheetData(Data): + """Balance Sheet Data.""" + + date: dateType = Field(description="Date of the fetched statement.") + symbol: Optional[str] = Field(description="Symbol of the company.") + cik: Optional[int] = Field(description="Central Index Key.") + currency: Optional[str] = Field(description="Reporting currency.") + filing_date: Optional[dateType] = Field(description="Filling date.") + accepted_date: Optional[datetime] = Field(description="Accepted date.") + period: Optional[str] = Field(description="Reporting period of the statement.") + + cash_and_cash_equivalents: Optional[int] = Field( + description="Cash and cash equivalents" + ) + short_term_investments: Optional[int] = Field(description="Short-term investments") + inventory: Optional[int] = Field(description="Inventory") + net_receivables: Optional[int] = Field(description="Receivables, net") + other_current_assets: Optional[int] = Field(description="Other current assets") + current_assets: Optional[int] = Field(description="Total current assets") + + long_term_investments: Optional[int] = Field(description="Long-term investments") + property_plant_equipment_net: Optional[int] = Field( + description="Property, plant and equipment, net" + ) + goodwill: Optional[int] = Field(description="Goodwill") + intangible_assets: Optional[int] = Field(description="Intangible assets") + other_non_current_assets: Optional[int] = Field( + description="Other non-current assets" + ) + tax_assets: Optional[int] = Field(description="Accrued income taxes") + other_assets: Optional[int] = Field(description="Other assets") + noncurrent_assets: Optional[int] + assets: Optional[int] + + account_payables: Optional[int] + other_current_liabilities: Optional[int] + tax_payables: Optional[int] = Field(description="Accrued income taxes") + deferred_revenue: Optional[int] = Field( + description="Accrued income taxes, other deferred revenue" + ) + short_term_debt: Optional[int] = Field( + description="Short-term borrowings, Long-term debt due within one year, Operating lease obligations due within one year, Finance lease obligations due within one year" + ) + current_liabilities: Optional[int] + + long_term_debt: Optional[int] = Field( + description="Long-term debt, Operating lease obligations, Long-term finance lease obligations" + ) + other_non_current_liabilities: Optional[int] = Field( + description="Deferred income taxes and other" + ) + other_liabilities: Optional[int] + noncurrent_liabilities: Optional[int] + liabilities: Optional[int] + + common_stock: Optional[int] + other_stockholder_equity: Optional[int] = Field( + description="Capital in excess of par value" + ) + + accumulated_other_comprehensive_income_loss: Optional[int] = Field( + description="Accumulated other comprehensive income (loss)" + ) + preferred_stock: Optional[int] = Field(description="Preferred stock") + retained_earnings: Optional[int] = Field(description="Retained earnings") + minority_interest: Optional[int] = Field(description="Minority interest") + total_stockholders_equity: Optional[int] + total_equity: Optional[int] + total_liabilities_and_stockholders_equity: Optional[int] + total_liabilities_and_total_equity: Optional[int] diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/balance_sheet_growth.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/balance_sheet_growth.py new file mode 100644 index 00000000000..356bc5ccb72 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/balance_sheet_growth.py @@ -0,0 +1,117 @@ +"""Balance Sheet Statement Growth Data Model.""" + + +from datetime import date as dateType + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + + +class BalanceSheetGrowthQueryParams(QueryParams, BaseSymbol): + """Balance Sheet Statement Growth Query.""" + + limit: int = Field(default=10, description=QUERY_DESCRIPTIONS.get("limit", "")) + + +class BalanceSheetGrowthData(Data, BaseSymbol): + """Balance Sheet Statement Growth Data.""" + + date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", "")) + period: str = Field(description="The period the statement is returned for.") + growth_cash_and_cash_equivalents: float = Field( + description="Growth rate of cash and cash equivalents." + ) + growth_short_term_investments: float = Field( + description="Growth rate of short-term investments." + ) + growth_cash_and_short_term_investments: float = Field( + description="Growth rate of cash and short-term investments." + ) + growth_net_receivables: float = Field(description="Growth rate of net receivables.") + growth_inventory: float = Field(description="Growth rate of inventory.") + growth_other_current_assets: float = Field( + description="Growth rate of other current assets." + ) + growth_total_current_assets: float = Field( + description="Growth rate of total current assets." + ) + growth_property_plant_equipment_net: float = Field( + description="Growth rate of net property, plant, and equipment." + ) + growth_goodwill: float = Field(description="Growth rate of goodwill.") + growth_intangible_assets: float = Field( + description="Growth rate of intangible assets." + ) + growth_goodwill_and_intangible_assets: float = Field( + description="Growth rate of goodwill and intangible assets." + ) + growth_long_term_investments: float = Field( + description="Growth rate of long-term investments." + ) + growth_tax_assets: float = Field(description="Growth rate of tax assets.") + growth_other_non_current_assets: float = Field( + description="Growth rate of other non-current assets." + ) + growth_total_non_current_assets: float = Field( + description="Growth rate of total non-current assets." + ) + growth_other_assets: float = Field(description="Growth rate of other assets.") + growth_total_assets: float = Field(description="Growth rate of total assets.") + growth_account_payables: float = Field( + description="Growth rate of accounts payable." + ) + growth_short_term_debt: float = Field(description="Growth rate of short-term debt.") + growth_tax_payables: float = Field(description="Growth rate of tax payables.") + growth_deferred_revenue: float = Field( + description="Growth rate of deferred revenue." + ) + growth_other_current_liabilities: float = Field( + description="Growth rate of other current liabilities." + ) + growth_total_current_liabilities: float = Field( + description="Growth rate of total current liabilities." + ) + growth_long_term_debt: float = Field(description="Growth rate of long-term debt.") + growth_deferred_revenue_non_current: float = Field( + description="Growth rate of non-current deferred revenue." + ) + growth_deferrred_tax_liabilities_non_current: float = Field( + description="Growth rate of non-current deferred tax liabilities." + ) + growth_other_non_current_liabilities: float = Field( + description="Growth rate of other non-current liabilities." + ) + growth_total_non_current_liabilities: float = Field( + description="Growth rate of total non-current liabilities." + ) + growth_other_liabilities: float = Field( + description="Growth rate of other liabilities." + ) + growth_total_liabilities: float = Field( + description="Growth rate of total liabilities." + ) + growth_common_stock: float = Field(description="Growth rate of common stock.") + growth_retained_earnings: float = Field( + description="Growth rate of retained earnings." + ) + growth_accumulated_other_comprehensive_income_loss: float = Field( + description="Growth rate of accumulated other comprehensive income/loss." + ) + growth_othertotal_stockholders_equity: float = Field( + description="Growth rate of other total stockholders' equity." + ) + growth_total_stockholders_equity: float = Field( + description="Growth rate of total stockholders' equity." + ) + growth_total_liabilities_and_stockholders_equity: float = Field( + description="Growth rate of total liabilities and stockholders' equity." + ) + growth_total_investments: float = Field( + description="Growth rate of total investments." + ) + growth_total_debt: float = Field(description="Growth rate of total debt.") + growth_net_debt: float = Field(description="Growth rate of net debt.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/base.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/base.py new file mode 100644 index 00000000000..2285fada451 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/base.py @@ -0,0 +1,23 @@ +from typing import Literal, Optional + +from pydantic import BaseModel, Field, NonNegativeInt, validator + +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS + + +class BaseSymbol(BaseModel): + symbol: str = Field(min_length=1, description=QUERY_DESCRIPTIONS.get("symbol", "")) + + @validator("symbol", pre=True) + def upper_symbol(cls, v: str): # pylint: disable=E0213 + return v.upper() + + +class FinancialStatementQueryParams(QueryParams, BaseSymbol): + period: Literal["annually", "quarterly"] = Field( + default="annually", description=QUERY_DESCRIPTIONS.get("period", "") + ) + limit: Optional[NonNegativeInt] = Field( + default=12, description=QUERY_DESCRIPTIONS.get("limit", "") + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/cash_flow_growth.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/cash_flow_growth.py new file mode 100644 index 00000000000..6f1ce754bb8 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/cash_flow_growth.py @@ -0,0 +1,104 @@ +"""Cash Flow Statement Growth Data Model.""" + + +from datetime import date as dateType + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + + +class CashFlowStatementGrowthQueryParams(QueryParams, BaseSymbol): + """Cash Flow Statement Growth Query.""" + + limit: int = Field(default=10, description=QUERY_DESCRIPTIONS.get("limit", "")) + + +class CashFlowStatementGrowthData(Data, BaseSymbol): + """Cash Flow Statement Growth Data.""" + + date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", "")) + period: str = Field(description="The period the statement is returned for.") + growth_net_income: float = Field(description="Growth rate of net income.") + growth_depreciation_and_amortization: float = Field( + description="Growth rate of depreciation and amortization." + ) + growth_deferred_income_tax: float = Field( + description="Growth rate of deferred income tax." + ) + growth_stock_based_compensation: float = Field( + description="Growth rate of stock-based compensation." + ) + growth_change_in_working_capital: float = Field( + description="Growth rate of change in working capital." + ) + growth_accounts_receivables: float = Field( + description="Growth rate of accounts receivables." + ) + growth_inventory: float = Field(description="Growth rate of inventory.") + growth_accounts_payables: float = Field( + description="Growth rate of accounts payables." + ) + growth_other_working_capital: float = Field( + description="Growth rate of other working capital." + ) + growth_other_non_cash_items: float = Field( + description="Growth rate of other non-cash items." + ) + growth_net_cash_provided_by_operating_activities: float = Field( + description="Growth rate of net cash provided by operating activities." + ) + growth_investments_in_property_plant_and_equipment: float = Field( + description="Growth rate of investments in property, plant, and equipment." + ) + growth_acquisitions_net: float = Field( + description="Growth rate of net acquisitions." + ) + growth_purchases_of_investments: float = Field( + description="Growth rate of purchases of investments." + ) + growth_sales_maturities_of_investments: float = Field( + description="Growth rate of sales maturities of investments." + ) + growth_other_investing_activities: float = Field( + description="Growth rate of other investing activities." + ) + growth_net_cash_used_for_investing_activities: float = Field( + description="Growth rate of net cash used for investing activities." + ) + growth_debt_repayment: float = Field(description="Growth rate of debt repayment.") + growth_common_stock_issued: float = Field( + description="Growth rate of common stock issued." + ) + growth_common_stock_repurchased: float = Field( + description="Growth rate of common stock repurchased." + ) + growth_dividends_paid: float = Field(description="Growth rate of dividends paid.") + growth_other_financing_activities: float = Field( + description="Growth rate of other financing activities." + ) + growth_net_cash_used_provided_by_financing_activities: float = Field( + description="Growth rate of net cash used/provided by financing activities." + ) + growth_effect_of_forex_changes_on_cash: float = Field( + description="Growth rate of the effect of foreign exchange changes on cash." + ) + growth_net_change_in_cash: float = Field( + description="Growth rate of net change in cash." + ) + growth_cash_at_end_of_period: float = Field( + description="Growth rate of cash at the end of the period." + ) + growth_cash_at_beginning_of_period: float = Field( + description="Growth rate of cash at the beginning of the period." + ) + growth_operating_cash_flow: float = Field( + description="Growth rate of operating cash flow." + ) + growth_capital_expenditure: float = Field( + description="Growth rate of capital expenditure." + ) + growth_free_cash_flow: float = Field(description="Growth rate of free cash flow.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/cash_flows.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/cash_flows.py new file mode 100644 index 00000000000..849606648a3 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/cash_flows.py @@ -0,0 +1,76 @@ +"""Cash Flow Statement Data Model.""" + + +from datetime import ( + date as dateType, + datetime, +) +from typing import Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.standard_models.base import FinancialStatementQueryParams + + +class CashFlowStatementQueryParams(FinancialStatementQueryParams): + """Cash Flow Statement Query.""" + + +class CashFlowStatementData(Data): + """Cash Flow Statement Data.""" + + date: dateType = Field(description="Date of the fetched statement.") + symbol: Optional[str] = Field(description="Symbol of the company.") + cik: Optional[int] = Field(description="Central Index Key.") + currency: Optional[str] = Field(description="Reporting currency.") + filing_date: Optional[dateType] = Field(description="Filling date.") + accepted_date: Optional[datetime] = Field(description="Accepted date.") + period: Optional[str] = Field(description="Reporting period of the statement.") + + cash_at_beginning_of_period: Optional[int] + net_income: Optional[int] + depreciation_and_amortization: Optional[int] + stock_based_compensation: Optional[int] + other_non_cash_items: Optional[int] = Field(description="Other non-cash items.") + deferred_income_tax: Optional[int] = Field(description="Deferred income tax.") + + inventory: Optional[int] + accounts_receivables: Optional[int] + + accounts_payables: Optional[int] + other_working_capital: Optional[int] = Field( + description="Accrued expenses and other, Unearned revenue." + ) + net_cash_flow_from_operating_activities: Optional[int] + + capital_expenditure: Optional[int] = Field( + description="Purchases of property and equipment." + ) + other_investing_activities: Optional[int] = Field( + description="Proceeds from property and equipment sales and incentives." + ) + acquisitions_net: Optional[int] = Field( + description="Acquisitions, net of cash acquired, and other" + ) + sales_maturities_of_investments: Optional[int] + purchases_of_investments: Optional[int] + net_cash_flow_from_investing_activities: Optional[int] + investments_in_property_plant_and_equipment: Optional[int] + + dividends_paid: Optional[int] + common_stock_repurchased: Optional[int] + debt_repayment: Optional[int] + + other_financing_activities: Optional[int] + + net_cash_flow_from_financing_activities: Optional[int] + effect_of_forex_changes_on_cash: Optional[int] = Field( + description="Foreign currency effect on cash, cash equivalents, and restricted cash" + ) + + net_change_in_cash: Optional[int] = Field( + description="Net increase (decrease) in cash, cash equivalents, and restricted cash" + ) + + cash_at_end_of_period: Optional[int] diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/company_overview.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/company_overview.py new file mode 100644 index 00000000000..0ddf5cdbed6 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/company_overview.py @@ -0,0 +1,64 @@ +"""Company Overview Data Model.""" + + +from datetime import date +from typing import Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol + + +class CompanyOverviewQueryParams(QueryParams, BaseSymbol): + """Company overview Query.""" + + +class CompanyOverviewData(Data, BaseSymbol): + """Company Overview Data. + + Returns the profile of a given company. + """ + + price: float = Field(description="The price of the company.") + beta: float = Field(description="The beta of the company.") + vol_avg: int = Field(description="The volume average of the company.") + mkt_cap: int = Field(description="The market capitalization of the company.") + last_div: float = Field(description="The last dividend of the company.") + range: str = Field(description="The range of the company.") + changes: float = Field(description="The changes of the company.") + company_name: str = Field(description="The company name of the company.") + currency: str = Field(description="The currency of the company.") + cik: Optional[str] = Field(description="The CIK of the company.") + isin: Optional[str] = Field(description="The ISIN of the company.") + cusip: Optional[str] = Field(description="The CUSIP of the company.") + exchange: str = Field(description="The exchange of the company.") + exchange_short_name: str = Field( + description="The exchange short name of the company." + ) + industry: str = Field(description="The industry of the company.") + website: str = Field(description="The website of the company.") + description: str = Field(description="The description of the company.") + ceo: str = Field(description="The CEO of the company.") + sector: str = Field(description="The sector of the company.") + country: str = Field(description="The country of the company.") + full_time_employees: str = Field( + description="The full time employees of the company." + ) + phone: str = Field(description="The phone of the company.") + address: str = Field(description="The address of the company.") + city: str = Field(description="The city of the company.") + state: str = Field(description="The state of the company.") + zip: str = Field(description="The zip of the company.") + dcf_diff: float = Field( + description="The discounted cash flow difference of the company." + ) + dcf: float = Field(description="The discounted cash flow of the company.") + image: str = Field(description="The image of the company.") + ipo_date: date = Field(description="The IPO date of the company.") + default_image: bool = Field(description="If the image is the default image.") + is_etf: bool = Field(description="If the company is an ETF.") + is_actively_trading: bool = Field(description="If the company is actively trading.") + is_adr: bool = Field(description="If the company is an ADR.") + is_fund: bool = Field(description="If the company is a fund.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/cpi.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/cpi.py new file mode 100644 index 00000000000..a80ab017d69 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/cpi.py @@ -0,0 +1,106 @@ +from datetime import date as dateType +from typing import List, Literal, Optional + +from pydantic import Field, validator + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + +CPI_COUNTRIES = Literal[ + "australia", + "austria", + "belgium", + "brazil", + "bulgaria", + "canada", + "chile", + "china", + "croatia", + "cyprus", + "czech_republic", + "denmark", + "estonia", + "euro_area", + "finland", + "france", + "germany", + "greece", + "hungary", + "iceland", + "india", + "indonesia", + "ireland", + "israel", + "italy", + "japan", + "korea", + "latvia", + "lithuania", + "luxembourg", + "malta", + "mexico", + "netherlands", + "new_zealand", + "norway", + "poland", + "portugal", + "romania", + "russian_federation", + "slovak_republic", + "slovakia", + "slovenia", + "south_africa", + "spain", + "sweden", + "switzerland", + "turkey", + "united_kingdom", + "united_states", +] + +CPI_UNITS = Literal["growth_previous", "growth_same", "index_2015"] + +CPI_FREQUENCY = Literal["monthly", "quarterly", "annual"] + + +class CPIQueryParams(QueryParams): + """CPI query.""" + + countries: List[CPI_COUNTRIES] = Field( + description=QUERY_DESCRIPTIONS.get("countries") + ) + units: CPI_UNITS = Field( + default="growth_same", description=QUERY_DESCRIPTIONS.get("units") + ) + frequency: CPI_FREQUENCY = Field( + default="monthly", description=QUERY_DESCRIPTIONS.get("frequency") + ) + harmonized: bool = Field( + default=False, description="Whether you wish to obtain harmonized data." + ) + start_date: Optional[dateType] = Field( + default=None, description=QUERY_DESCRIPTIONS.get("start_date") + ) + end_date: Optional[dateType] = Field( + default=None, description=QUERY_DESCRIPTIONS.get("end_date") + ) + + +class CPIData(Data): + """CPI data.""" + + date: dateType = Field(description=DATA_DESCRIPTIONS.get("date")) + realtime_start: dateType = Field( + description="The date the data was updated." + ) # TODO: What is this? + realtime_end: dateType = Field( + description="The date the data was updated." + ) # TODO: What is this? + value: float = Field(description="The value of the data.") + + @validator("value", pre=True) + def value_validate(cls, v: str): # pylint: disable=E0213 + if v == ".": + return 0.0 + return float(v) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/crypto_eod.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/crypto_eod.py new file mode 100644 index 00000000000..e7e53ad3a2e --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/crypto_eod.py @@ -0,0 +1,37 @@ +"""Crypto aggregate end of day price data model.""" + + +from datetime import date, datetime +from typing import Optional + +from pydantic import Field, PositiveFloat + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + + +class CryptoEODQueryParams(QueryParams, BaseSymbol): + """Crypto end of day Query.""" + + start_date: Optional[date] = Field( + default=None, + description=QUERY_DESCRIPTIONS.get("start_date", ""), + ) + end_date: Optional[date] = Field( + default=None, + description=QUERY_DESCRIPTIONS.get("end_date", ""), + ) + + +class CryptoEODData(Data): + """Crypto end of day price Data.""" + + date: datetime = Field(description=DATA_DESCRIPTIONS.get("date", "")) + open: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("open", "")) + high: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("high", "")) + low: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("low", "")) + close: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("close", "")) + volume: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("volume", "")) + vwap: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("vwap", "")) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/dividend_calendar.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/dividend_calendar.py new file mode 100644 index 00000000000..eac6ab0a415 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/dividend_calendar.py @@ -0,0 +1,45 @@ +"""Dividend Calendar data model.""" + + +from datetime import date as dateType +from typing import Optional + +from pydantic import Field, NonNegativeFloat + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + + +class DividendCalendarQueryParams(QueryParams): + """Dividend Calendar Query.""" + + start_date: Optional[dateType] = Field( + description=QUERY_DESCRIPTIONS.get("start_date", "") + ) + end_date: Optional[dateType] = Field( + description=QUERY_DESCRIPTIONS.get("end_date", "") + ) + + +class DividendCalendarData(Data, BaseSymbol): + """Dividend Calendar Data.""" + + date: dateType = Field(description=DATA_DESCRIPTIONS.get("date")) + label: str = Field(description="The date in human readable form in the calendar.") + adj_dividend: Optional[NonNegativeFloat] = Field( + description="The adjusted dividend on a date in the calendar.", + ) + dividend: Optional[NonNegativeFloat] = Field( + description="The dividend amount in the calendar." + ) + record_date: Optional[dateType] = Field( + description="The record date of the dividend in the calendar.", + ) + payment_date: Optional[dateType] = Field( + description="The payment date of the dividend in the calendar.", + ) + declaration_date: Optional[dateType] = Field( + description="The declaration date of the dividend in the calendar.", + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/earnings_calendar.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/earnings_calendar.py new file mode 100644 index 00000000000..dce2384eb86 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/earnings_calendar.py @@ -0,0 +1,43 @@ +"""Earnings calendar data model.""" + + +from datetime import date as dateType +from typing import Optional + +from pydantic import Field, NonNegativeFloat + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + + +class EarningsCalendarQueryParams(QueryParams, BaseSymbol): + """Earnings calendar rating Query.""" + + limit: Optional[int] = Field( + default=50, description=QUERY_DESCRIPTIONS.get("limit", "") + ) + + +class EarningsCalendarData(Data, BaseSymbol): + """Earnings calendar Data.""" + + date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", "")) + eps: Optional[NonNegativeFloat] = Field( + description="The EPS of the earnings calendar." + ) + eps_estimated: Optional[NonNegativeFloat] = Field( + description="The estimated EPS of the earnings calendar." + ) + time: str = Field(description="The time of the earnings calendar.") + revenue: Optional[int] = Field(description="The revenue of the earnings calendar.") + revenue_estimated: Optional[int] = Field( + description="The estimated revenue of the earnings calendar." + ) + updated_from_date: Optional[dateType] = Field( + description="The updated from date of the earnings calendar." + ) + fiscal_date_ending: dateType = Field( + description="The fiscal date ending of the earnings calendar." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/earnings_call_transcript.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/earnings_call_transcript.py new file mode 100644 index 00000000000..dcdb9ecaadd --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/earnings_call_transcript.py @@ -0,0 +1,30 @@ +"""Earnings call transcript data model.""" + + +from datetime import datetime +from typing import Literal + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS + + +class EarningsCallTranscriptQueryParams(QueryParams, BaseSymbol): + """Earnings call transcript rating Query.""" + + year: int = Field(description="The year of the earnings call transcript.") + quarter: Literal[1, 2, 3, 4] = Field( + default=1, description="The quarter of the earnings call transcript." + ) + + +class EarningsCallTranscriptData(Data, BaseSymbol): + """Earnings call transcript Data.""" + + quarter: int = Field(description="The quarter of the earnings call transcript.") + year: int = Field(description="The year of the earnings call transcript.") + date: datetime = Field(description=DATA_DESCRIPTIONS.get("date", "")) + content: str = Field(description="The content of the earnings call transcript.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/esg_risk_rating.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/esg_risk_rating.py similarity index 85% rename from openbb_sdk/sdk/provider/openbb_provider/models/esg_risk_rating.py rename to openbb_sdk/sdk/provider/openbb_provider/standard_models/esg_risk_rating.py index 21173dd3550..2ba7b8bf610 100644 --- a/openbb_sdk/sdk/provider/openbb_provider/models/esg_risk_rating.py +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/esg_risk_rating.py @@ -3,8 +3,9 @@ from typing import Literal -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol class ESGRiskRatingQueryParams(QueryParams, BaseSymbol): diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/esg_score.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/esg_score.py similarity index 87% rename from openbb_sdk/sdk/provider/openbb_provider/models/esg_score.py rename to openbb_sdk/sdk/provider/openbb_provider/standard_models/esg_score.py index 20c428799ff..9223c9c33c0 100644 --- a/openbb_sdk/sdk/provider/openbb_provider/models/esg_score.py +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/esg_score.py @@ -3,8 +3,9 @@ from datetime import date, datetime -from openbb_provider.abstract.data import Data, QueryParams -from openbb_provider.models.base import BaseSymbol +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol class ESGScoreQueryParams(QueryParams, BaseSymbol): diff --git a/openbb_sdk/sdk/provider/openbb_provider/models/esg_sector.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/esg_sector.py similarity index 88% rename from openbb_sdk/sdk/provider/openbb_provider/models/esg_sector.py rename to openbb_sdk/sdk/provider/openbb_provider/standard_models/esg_sector.py index 01f230fa6c8..296d79f3203 100644 --- a/openbb_sdk/sdk/provider/openbb_provider/models/esg_sector.py +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/esg_sector.py @@ -1,7 +1,8 @@ """ESG Sector data model.""" -from openbb_provider.abstract.data import Data, QueryParams +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams class ESGSectorQueryParams(QueryParams): diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/executive_compensation.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/executive_compensation.py new file mode 100644 index 00000000000..9ca2101a2f7 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/executive_compensation.py @@ -0,0 +1,45 @@ +"""Executive Compensation Data Model.""" + + +from datetime import ( + date as dateType, + datetime, +) +from typing import Optional + +from pydantic import Field, NonNegativeFloat, PositiveFloat + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol + + +class ExecutiveCompensationQueryParams(QueryParams, BaseSymbol): + """Executive Compensation Query.""" + + +class ExecutiveCompensationData(Data, BaseSymbol): + """Return Executive Compensation Data.""" + + cik: Optional[str] = Field( + description="The Central Index Key (CIK) of the company." + ) + filing_date: dateType = Field(description="The date of the filing.") + accepted_date: datetime = Field(description="The date the filing was accepted.") + name_and_position: str = Field( + description="The name and position of the executive." + ) + year: int = Field(description="The year of the compensation.") + salary: PositiveFloat = Field(description="The salary of the executive.") + bonus: NonNegativeFloat = Field(description="The bonus of the executive.") + stock_award: NonNegativeFloat = Field( + description="The stock award of the executive." + ) + incentive_plan_compensation: NonNegativeFloat = Field( + description="The incentive plan compensation of the executive." + ) + all_other_compensation: NonNegativeFloat = Field( + description="The all other compensation of the executive." + ) + total: PositiveFloat = Field(description="The total compensation of the executive.") + url: str = Field(description="The URL of the filing data.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/forex_eod.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/forex_eod.py new file mode 100644 index 00000000000..7101bb96311 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/forex_eod.py @@ -0,0 +1,37 @@ +"""Forex aggregate end of day price data model.""" + + +from datetime import date, datetime +from typing import Optional + +from pydantic import Field, NonNegativeFloat, PositiveFloat + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + + +class ForexEODQueryParams(QueryParams, BaseSymbol): + """Forex end of day Query.""" + + start_date: Optional[date] = Field( + default=None, + description=QUERY_DESCRIPTIONS.get("start_date", ""), + ) + end_date: Optional[date] = Field( + default=None, + description=QUERY_DESCRIPTIONS.get("end_date", ""), + ) + + +class ForexEODData(Data): + """Forex end of day price Data.""" + + date: datetime = Field(description=DATA_DESCRIPTIONS.get("date", "")) + open: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("open", "")) + high: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("high", "")) + low: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("low", "")) + close: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("close", "")) + volume: NonNegativeFloat = Field(description=DATA_DESCRIPTIONS.get("volume", "")) + vwap: Optional[PositiveFloat] = Field(description=DATA_DESCRIPTIONS.get("vwap", "")) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/forex_pairs.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/forex_pairs.py new file mode 100644 index 00000000000..969474c5073 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/forex_pairs.py @@ -0,0 +1,17 @@ +"""Forex available pairs data model.""" + + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams + + +class ForexPairsQueryParams(QueryParams): + """Forex available pairs Query.""" + + +class ForexPairsData(Data): + """Forex available pairs Data.""" + + name: str = Field(description="The name of the currency pair.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/global_news.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/global_news.py new file mode 100644 index 00000000000..b0a258821b7 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/global_news.py @@ -0,0 +1,26 @@ +"""Global News Data Model.""" + + +from datetime import datetime +from typing import Optional + +from pydantic import Field, NonNegativeInt + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams + + +class GlobalNewsQueryParams(QueryParams): + """Global news query.""" + + page: NonNegativeInt = Field(default=0, description="The page of the global news.") + + +class GlobalNewsData(Data): + """Return Global News Data.""" + + date: datetime = Field(description="The published date of the news.") + title: str = Field(description="The title of the news.") + image: Optional[str] = Field(description="The image URL of the news.") + text: str = Field(description="The text/body of the news.") + url: str = Field(description="The URL of the news.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_dividends.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_dividends.py new file mode 100644 index 00000000000..148e0c0fd59 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_dividends.py @@ -0,0 +1,35 @@ +"""Historical dividends data model.""" + + +from datetime import date as dateType +from typing import Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol + + +class HistoricalDividendsQueryParams(QueryParams, BaseSymbol): + """Historical dividends query.""" + + +class HistoricalDividendsData(Data): + """Historical dividends data.""" + + date: dateType = Field(description="The date of the historical dividends.") + label: str = Field(description="The label of the historical dividends.") + adj_dividend: float = Field( + description="The adjusted dividend of the historical dividends." + ) + dividend: float = Field(description="The dividend of the historical dividends.") + record_date: Optional[dateType] = Field( + description="The record date of the historical dividends." + ) + payment_date: Optional[dateType] = Field( + description="The payment date of the historical dividends." + ) + declaration_date: Optional[dateType] = Field( + description="The declaration date of the historical dividends." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_employees.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_employees.py new file mode 100644 index 00000000000..ab480f08113 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_employees.py @@ -0,0 +1,37 @@ +"""Historical Employees data model.""" + + +from datetime import date, datetime + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol + + +class HistoricalEmployeesQueryParams(QueryParams, BaseSymbol): + """Historical Employees Query.""" + + +class HistoricalEmployeesData(Data, BaseSymbol): + """Historical Employees Data.""" + + cik: int = Field( + description="The CIK of the company to retrieve the historical employees of." + ) + acceptance_time: datetime = Field( + description="The time of acceptance of the company employee." + ) + period_of_report: date = Field( + description="The date of reporting of the company employee." + ) + company_name: str = Field( + description="The registered name of the company to retrieve the historical employees of." + ) + form_type: str = Field(description="The form type of the company employee.") + filing_date: date = Field(description="The filing date of the company employee") + employee_count: int = Field(description="The count of employees of the company.") + source: str = Field( + description="The source URL which retrieves this data for the company." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_stock_splits.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_stock_splits.py new file mode 100644 index 00000000000..5188de57eb1 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_stock_splits.py @@ -0,0 +1,28 @@ +"""Historical Stock Splits data model.""" + + +from datetime import date as dateType + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS + + +class HistoricalStockSplitsQueryParams(QueryParams, BaseSymbol): + """Historical Stock Splits Query.""" + + +class HistoricalStockSplitsData(Data): + """Historical Stock Splits Data.""" + + date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", "")) + label: str = Field(description="The label of the historical stock splits.") + numerator: float = Field( + description="The numerator of the historical stock splits." + ) + denominator: float = Field( + description="The denominator of the historical stock splits." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/income_statement.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/income_statement.py new file mode 100644 index 00000000000..01c711611e6 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/income_statement.py @@ -0,0 +1,74 @@ +"""Income Statement Data Model.""" + + +from datetime import ( + date as dateType, + datetime, +) +from typing import Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.standard_models.base import FinancialStatementQueryParams + + +class IncomeStatementQueryParams(FinancialStatementQueryParams): + """Income Statement Query.""" + + +class IncomeStatementData(Data): + """Income Statement Data.""" + + date: dateType = Field(description="Date of the income statement.") + symbol: str = Field(description="Symbol of the company.") + cik: Optional[int] = Field(description="Central Index Key.") + currency: Optional[str] = Field(description="Reporting currency.") + filing_date: Optional[dateType] = Field(description="Filling date.") + accepted_date: Optional[datetime] = Field(description="Accepted date.") + period: Optional[str] = Field(description="Period of the income statement.") + + revenue: Optional[int] = Field(description="Revenue.") + cost_of_revenue: Optional[int] = Field(description="Cost of revenue.") + gross_profit: Optional[int] = Field(description="Gross profit.") + + research_and_development_expenses: Optional[int] = Field( + description="Research and development expenses." + ) + general_and_administrative_expenses: Optional[int] = Field( + description="General and administrative expenses." + ) + selling_and_marketing_expenses: Optional[float] = Field( + description="Selling and marketing expenses." + ) + selling_general_and_administrative_expenses: Optional[int] = Field( + description="Selling, general and administrative expenses." + ) + other_expenses: Optional[int] = Field(description="Other expenses.") + operating_expenses: Optional[int] = Field(description="Operating expenses.") + + depreciation_and_amortization: Optional[int] = Field( + description="Depreciation and amortization." + ) + ebitda: Optional[int] = Field( + description="Earnings before interest, taxes, depreciation and amortization." + ) + operating_income: Optional[int] = Field(description="Operating income.") + + interest_income: Optional[int] = Field(description="Interest income.") + interest_expense: Optional[int] = Field(description="Interest expense.") + total_other_income_expenses_net: Optional[int] = Field( + description="Total other income expenses net." + ) + income_before_tax: Optional[int] = Field(description="Income before tax.") + income_tax_expense: Optional[int] = Field(description="Income tax expense.") + + net_income: Optional[int] = Field(description="Net income.") + eps: Optional[float] = Field(description="Earnings per share.") + eps_diluted: Optional[float] = Field(description="Earnings per share diluted.") + weighted_average_shares_outstanding: Optional[int] = Field( + description="Weighted average shares outstanding." + ) + weighted_average_shares_outstanding_dil: Optional[int] = Field( + description="Weighted average shares outstanding diluted." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/income_statement_growth.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/income_statement_growth.py new file mode 100644 index 00000000000..cd4a9834538 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/income_statement_growth.py @@ -0,0 +1,98 @@ +"""Income Statement Growth Data Model.""" + + +from datetime import date as dateType +from typing import Literal + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + + +class IncomeStatementGrowthQueryParams(QueryParams, BaseSymbol): + """Income Statement Growth Query.""" + + limit: int = Field(default=10, description=QUERY_DESCRIPTIONS.get("limit", "")) + period: Literal["annually", "quarterly"] = Field( + default="annually", description=QUERY_DESCRIPTIONS.get("period", "") + ) + + +class IncomeStatementGrowthData(Data, BaseSymbol): + """Income Statement Growth Data.""" + + date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", "")) + period: str = Field(description="The period the statement is returned for.") + growth_revenue: float = Field(description="Growth rate of total revenue.") + growth_cost_of_revenue: float = Field( + description="Growth rate of cost of goods sold." + ) + growth_gross_profit: float = Field(description="Growth rate of gross profit.") + growth_gross_profit_ratio: float = Field( + description="Growth rate of gross profit as a percentage of revenue." + ) + growth_research_and_development_expenses: float = Field( + description="Growth rate of expenses on research and development." + ) + growth_general_and_administrative_expenses: float = Field( + description="Growth rate of general and administrative expenses." + ) + growth_selling_and_marketing_expenses: float = Field( + description="Growth rate of expenses on selling and marketing activities." + ) + growth_other_expenses: float = Field( + description="Growth rate of other operating expenses." + ) + growth_operating_expenses: float = Field( + description="Growth rate of total operating expenses." + ) + growth_cost_and_expenses: float = Field( + description="Growth rate of total costs and expenses." + ) + growth_interest_expense: float = Field( + description="Growth rate of interest expenses." + ) + growth_depreciation_and_amortization: float = Field( + description="Growth rate of depreciation and amortization expenses." + ) + growth_ebitda: float = Field( + description="Growth rate of Earnings Before Interest, Taxes, Depreciation, and Amortization." + ) + growth_ebitda_ratio: float = Field( + description="Growth rate of EBITDA as a percentage of revenue." + ) + growth_operating_income: float = Field( + description="Growth rate of operating income." + ) + growth_operating_income_ratio: float = Field( + description="Growth rate of operating income as a percentage of revenue." + ) + growth_total_other_income_expenses_net: float = Field( + description="Growth rate of net total other income and expenses." + ) + growth_income_before_tax: float = Field( + description="Growth rate of income before taxes." + ) + growth_income_before_tax_ratio: float = Field( + description="Growth rate of income before taxes as a percentage of revenue." + ) + growth_income_tax_expense: float = Field( + description="Growth rate of income tax expenses." + ) + growth_net_income: float = Field(description="Growth rate of net income.") + growth_net_income_ratio: float = Field( + description="Growth rate of net income as a percentage of revenue." + ) + growth_eps: float = Field(description="Growth rate of Earnings Per Share (EPS).") + growth_eps_diluted: float = Field( + description="Growth rate of diluted Earnings Per Share (EPS)." + ) + growth_weighted_average_shs_out: float = Field( + description="Growth rate of weighted average shares outstanding." + ) + growth_weighted_average_shs_out_dil: float = Field( + description="Growth rate of diluted weighted average shares outstanding." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/institutional_ownership.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/institutional_ownership.py new file mode 100644 index 00000000000..3aff6ff05ae --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/institutional_ownership.py @@ -0,0 +1,113 @@ +"""Institutional Ownership Data Model.""" + + +from datetime import date as dateType +from typing import Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + + +class InstitutionalOwnershipQueryParams(QueryParams, BaseSymbol): + """Institutional Ownership Query.""" + + include_current_quarter: bool = Field( + default=False, description="Include current quarter data." + ) + date: Optional[dateType] = Field(description=QUERY_DESCRIPTIONS.get("date", "")) + + +class InstitutionalOwnershipData(Data, BaseSymbol): + """Institutional Ownership Data.""" + + cik: Optional[str] = Field(description="The CIK of the company.") + date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", "")) + investors_holding: int = Field( + description="The number of investors holding the stock." + ) + last_investors_holding: int = Field( + description="The number of investors holding the stock in the last quarter." + ) + investors_holding_change: int = Field( + description="The change in the number of investors holding the stock." + ) + number_of_13f_shares: Optional[int] = Field(description="The number of 13F shares.") + last_number_of_13f_shares: Optional[int] = Field( + description="The number of 13F shares in the last quarter." + ) + number_of_13f_shares_change: Optional[int] = Field( + description="The change in the number of 13F shares." + ) + total_invested: float = Field(description="The total amount invested.") + last_total_invested: float = Field( + description="The total amount invested in the last quarter." + ) + total_invested_change: float = Field( + description="The change in the total amount invested." + ) + ownership_percent: float = Field(description="The ownership percent.") + last_ownership_percent: float = Field( + description="The ownership percent in the last quarter." + ) + ownership_percent_change: float = Field( + description="The change in the ownership percent." + ) + new_positions: int = Field(description="The number of new positions.") + last_new_positions: int = Field( + description="The number of new positions in the last quarter." + ) + new_positions_change: int = Field( + description="he change in the number of new positions." + ) + increased_positions: int = Field(description="The number of increased positions.") + last_increased_positions: int = Field( + description="The number of increased positions in the last quarter." + ) + increased_positions_change: int = Field( + description="The change in the number of increased positions." + ) + closed_positions: int = Field(description="The number of closed positions.") + last_closed_positions: int = Field( + description="The number of closed positions in the last quarter." + ) + closed_positions_change: int = Field( + description="The change in the number of closed positions." + ) + reduced_positions: int = Field(description="The number of reduced positions.") + last_reduced_positions: int = Field( + description="The number of reduced positions in the last quarter." + ) + reduced_positions_change: int = Field( + description="The change in the number of reduced positions." + ) + total_calls: int = Field( + description="Total number of call options contracts traded for Apple Inc. on the specified date." + ) + last_total_calls: int = Field( + description="Total number of call options contracts traded for Apple Inc. on the previous reporting date." + ) + total_calls_change: int = Field( + description="Change in the total number of call options contracts traded between the current and previous reporting dates." + ) + total_puts: int = Field( + description="Total number of put options contracts traded for Apple Inc. on the specified date." + ) + last_total_puts: int = Field( + description="Total number of put options contracts traded for Apple Inc. on the previous reporting date." + ) + total_puts_change: int = Field( + description="Change in the total number of put options contracts traded between the current and previous reporting dates." + ) + put_call_ratio: float = Field( + description="The put-call ratio, which is the ratio of the total number of put options to call options traded on the specified date." + ) + last_put_call_ratio: float = Field( + description="The put-call ratio on the previous reporting date." + ) + put_call_ratio_change: float = Field( + description="Change in the put-call ratio between the current and previous reporting dates." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/key_executives.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/key_executives.py new file mode 100644 index 00000000000..e2768fdd74d --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/key_executives.py @@ -0,0 +1,26 @@ +"""Key Executives Data Model.""" + + +from typing import Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol + + +class KeyExecutivesQueryParams(QueryParams, BaseSymbol): + """Key Executives Query.""" + + +class KeyExecutivesData(Data): + """Key Executives Data.""" + + title: str = Field(description="Designation of the key executive.") + name: str = Field(description="Name of the key executive.") + pay: Optional[int] = Field(description="Pay of the key executive.") + currency_pay: str = Field(description="The currency of the pay.") + gender: Optional[str] = Field(description="Gender of the key executive.") + year_born: Optional[str] = Field(description="Birth year of the key executive.") + title_since: Optional[int] = Field(description="Date the tile was held since.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/key_metrics.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/key_metrics.py new file mode 100644 index 00000000000..2f9ff97a26c --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/key_metrics.py @@ -0,0 +1,133 @@ +"""Key Metrics Data Model.""" + + +from datetime import date as dateType +from typing import Literal, Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + + +class KeyMetricsQueryParams(QueryParams, BaseSymbol): + """Key Metrics Query.""" + + period: Literal["annually", "quarterly"] = Field( + default="annually", description=QUERY_DESCRIPTIONS.get("period", "") + ) + limit: Optional[int] = Field( + default=100, description=QUERY_DESCRIPTIONS.get("limit", "") + ) + + +class KeyMetricsData(Data, BaseSymbol): + """Key Metrics Data.""" + + date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", "")) + period: str = Field(description="The period of the data.") + revenue_per_share: Optional[float] = Field(description="Revenue per share") + net_income_per_share: Optional[float] = Field(description="Net income per share") + operating_cash_flow_per_share: Optional[float] = Field( + description="Operating cash flow per share" + ) + free_cash_flow_per_share: Optional[float] = Field( + description="Free cash flow per share" + ) + cash_per_share: Optional[float] = Field(description="Cash per share") + book_value_per_share: Optional[float] = Field(description="Book value per share") + tangible_book_value_per_share: Optional[float] = Field( + description="Tangible book value per share" + ) + shareholders_equity_per_share: Optional[float] = Field( + description="Shareholders equity per share" + ) + interest_debt_per_share: Optional[float] = Field( + description="Interest debt per share" + ) + market_cap: Optional[float] = Field(description="Market capitalization") + enterprise_value: Optional[float] = Field(description="Enterprise value") + pe_ratio: Optional[float] = Field(description="Price-to-earnings ratio (P/E ratio)") + price_to_sales_ratio: Optional[float] = Field(description="Price-to-sales ratio") + pocf_ratio: Optional[float] = Field( + description="Price-to-operating cash flow ratio" + ) + pfcf_ratio: Optional[float] = Field(description="Price-to-free cash flow ratio") + pb_ratio: Optional[float] = Field(description="Price-to-book ratio") + ptb_ratio: Optional[float] = Field(description="Price-to-tangible book ratio") + ev_to_sales: Optional[float] = Field(description="Enterprise value-to-sales ratio") + enterprise_value_over_ebitda: Optional[float] = Field( + description="Enterprise value-to-EBITDA ratio" + ) + ev_to_operating_cash_flow: Optional[float] = Field( + description="Enterprise value-to-operating cash flow ratio" + ) + ev_to_free_cash_flow: Optional[float] = Field( + description="Enterprise value-to-free cash flow ratio" + ) + earnings_yield: Optional[float] = Field(description="Earnings yield") + free_cash_flow_yield: Optional[float] = Field(description="Free cash flow yield") + debt_to_equity: Optional[float] = Field(description="Debt-to-equity ratio") + debt_to_assets: Optional[float] = Field(description="Debt-to-assets ratio") + net_debt_to_ebitda: Optional[float] = Field(description="Net debt-to-EBITDA ratio") + current_ratio: Optional[float] = Field(description="Current ratio") + interest_coverage: Optional[float] = Field(description="Interest coverage") + income_quality: Optional[float] = Field(description="Income quality") + dividend_yield: Optional[float] = Field(description="Dividend yield") + payout_ratio: Optional[float] = Field(description="Payout ratio") + sales_general_and_administrative_to_revenue: Optional[float] = Field( + description="Sales general and administrative expenses-to-revenue ratio" + ) + research_and_development_to_revenue: Optional[float] = Field( + description="Research and development expenses-to-revenue ratio" + ) + intangibles_to_total_assets: Optional[float] = Field( + description="Intangibles-to-total assets ratio" + ) + capex_to_operating_cash_flow: Optional[float] = Field( + description="Capital expenditures-to-operating cash flow ratio" + ) + capex_to_revenue: Optional[float] = Field( + description="Capital expenditures-to-revenue ratio" + ) + capex_to_depreciation: Optional[float] = Field( + description="Capital expenditures-to-depreciation ratio" + ) + stock_based_compensation_to_revenue: Optional[float] = Field( + description="Stock-based compensation-to-revenue ratio" + ) + graham_number: Optional[float] = Field(description="Graham number") + roic: Optional[float] = Field(description="Return on invested capital") + return_on_tangible_assets: Optional[float] = Field( + description="Return on tangible assets" + ) + graham_net_net: Optional[float] = Field( + description="Graham net-net working capital" + ) + working_capital: Optional[float] = Field(description="Working capital") + tangible_asset_value: Optional[float] = Field(description="Tangible asset value") + net_current_asset_value: Optional[float] = Field( + description="Net current asset value" + ) + invested_capital: Optional[float] = Field(description="Invested capital") + average_receivables: Optional[float] = Field(description="Average receivables") + average_payables: Optional[float] = Field(description="Average payables") + average_inventory: Optional[float] = Field(description="Average inventory") + days_sales_outstanding: Optional[float] = Field( + description="Days sales outstanding" + ) + days_payables_outstanding: Optional[float] = Field( + description="Days payables outstanding" + ) + days_of_inventory_on_hand: Optional[float] = Field( + description="Days of inventory on hand" + ) + receivables_turnover: Optional[float] = Field(description="Receivables turnover") + payables_turnover: Optional[float] = Field(description="Payables turnover") + inventory_turnover: Optional[float] = Field(description="Inventory turnover") + roe: Optional[float] = Field(description="Return on equity") + capex_per_share: Optional[float] = Field( + description="Capital expenditures per share" + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/major_indices_constituents.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/major_indices_constituents.py new file mode 100644 index 00000000000..7b92c92462c --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/major_indices_constituents.py @@ -0,0 +1,44 @@ +"""Major Indices Constituents data model.""" + + +from datetime import date +from typing import Literal, Optional, Union + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol + + +class MajorIndicesConstituentsQueryParams(QueryParams): + """Major Indices Constituents Query.""" + + index: Literal["nasdaq", "sp500", "dowjones"] = Field( + default="dowjones", + description="The index for which we want to fetch the constituents.", + ) + + +class MajorIndicesConstituentsData(Data, BaseSymbol): + """Major Indices Constituents Data.""" + + name: str = Field(description="The name of the constituent company in the index.") + sector: str = Field( + description="The sector the constituent company in the index belongs to." + ) + sub_sector: Optional[str] = Field( + description="The sub-sector the constituent company in the index belongs to." + ) + headquarter: Optional[str] = Field( + description="The location of the headquarter of the constituent company in the index." + ) + date_first_added: Optional[Union[date, str]] = Field( + description="The date the constituent company was added to the index." + ) + cik: int = Field( + description="The Central Index Key of the constituent company in the index." + ) + founded: Union[date, str] = Field( + description="The founding year of the constituent company in the index." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/major_indices_eod.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/major_indices_eod.py new file mode 100644 index 00000000000..423722972ad --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/major_indices_eod.py @@ -0,0 +1,33 @@ +"""Major indices aggregate end of day price data model.""" + + +from datetime import date +from typing import Optional + +from pydantic import Field, PositiveFloat + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + + +class MajorIndicesEODQueryParams(QueryParams, BaseSymbol): + """Major Indices end of day Query.""" + + start_date: Optional[date] = Field( + description=QUERY_DESCRIPTIONS.get("start_date", ""), + ) + end_date: Optional[date] = Field( + description=QUERY_DESCRIPTIONS.get("end_date", ""), + ) + + +class MajorIndicesEODData(Data): + """Major Indices end of day price data.""" + + open: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("open", "")) + high: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("high", "")) + low: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("low", "")) + close: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("close", "")) + volume: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("volume", "")) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/price_target.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/price_target.py new file mode 100644 index 00000000000..f6791d2f19a --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/price_target.py @@ -0,0 +1,38 @@ +"""Price Target data model.""" + + +from datetime import datetime +from typing import Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol + + +class PriceTargetQueryParams(QueryParams, BaseSymbol): + """Price Target Query.""" + + +class PriceTargetData(Data, BaseSymbol): + """Price target Data.""" + + published_date: datetime = Field( + description="The published date of the price target." + ) + news_url: str = Field(description="The news URL of the price target.") + news_title: Optional[str] = Field(description="The news title of the price target.") + analyst_name: Optional[str] = Field( + description="The analyst name of the price target." + ) + price_target: float = Field(description="The price target of the price target.") + adj_price_target: float = Field( + description="The adjusted price target of the price target." + ) + price_when_posted: float = Field( + description="The price when posted of the price target." + ) + news_publisher: str = Field(description="The news publisher of the price target.") + news_base_url: str = Field(description="The news base URL of the price target.") + analyst_company: str = Field(description="The analyst company of the price target.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/price_target_consensus.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/price_target_consensus.py new file mode 100644 index 00000000000..c8bb9d33546 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/price_target_consensus.py @@ -0,0 +1,29 @@ +"""Price target consensus data model.""" + + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol + + +class PriceTargetConsensusQueryParams(QueryParams, BaseSymbol): + """Price Target Consensus Query.""" + + +class PriceTargetConsensusData(Data, BaseSymbol): + """Price target consensus Data.""" + + target_high: float = Field( + description="The high target of the price target consensus." + ) + target_low: float = Field( + description="The low target of the price target consensus." + ) + target_consensus: float = Field( + description="The consensus target of the price target consensus." + ) + target_median: float = Field( + description="The median target of the price target consensus." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/revenue_business_line.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/revenue_business_line.py new file mode 100644 index 00000000000..f6d10dcb459 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/revenue_business_line.py @@ -0,0 +1,32 @@ +"""Revenue by Business Line data model.""" + + +from datetime import date as dateType +from typing import Dict, Literal + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + + +class RevenueBusinessLineQueryParams(QueryParams, BaseSymbol): + """Revenue Business Line Query.""" + + period: Literal["quarterly", "annually"] = Field( + default="annually", description=QUERY_DESCRIPTIONS.get("period", "") + ) + structure: Literal["hierarchical", "flat"] = Field( + default="flat", description="The structure of the returned data." + ) # should always be flat + + +class RevenueBusinessLineData(Data): + """Revenue by Business Line Data.""" + + date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", "")) + business_line: Dict[str, int] = Field( + description="Day level data containing the revenue of the business line." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/revenue_geographic.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/revenue_geographic.py new file mode 100644 index 00000000000..d145191f096 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/revenue_geographic.py @@ -0,0 +1,42 @@ +"""Revenue by geographic segments data model.""" + + +from datetime import date as dateType +from typing import Literal, Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + + +class RevenueGeographicQueryParams(QueryParams, BaseSymbol): + """Revenue by Geographic Segments Query.""" + + period: Literal["quarterly", "annually"] = Field( + default="annually", description=QUERY_DESCRIPTIONS.get("period", "") + ) + structure: Literal["hierarchical", "flat"] = Field( + default="flat", description="The structure of the returned data." + ) # should always be flat # should always be flat + + +class RevenueGeographicData(Data): + """Revenue by Geographic Segments Data.""" + + date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", "")) + americas: Optional[int] = Field( + description="The revenue from the the American segment." + ) + europe: Optional[int] = Field( + description="The revenue from the the European segment." + ) + greater_china: Optional[int] = Field( + description="The revenue from the the Greater China segment." + ) + japan: Optional[int] = Field(description="The revenue from the the Japan segment.") + rest_of_asia_pacific: Optional[int] = Field( + description="The revenue from the the Rest of Asia Pacific segment." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/risk_premium.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/risk_premium.py new file mode 100644 index 00000000000..599fa0288f4 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/risk_premium.py @@ -0,0 +1,26 @@ +"""Risk Premium data model.""" + + +from typing import Optional + +from pydantic import Field, NonNegativeFloat, PositiveFloat + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams + + +class RiskPremiumQueryParams(QueryParams): + """Risk Premium Query.""" + + +class RiskPremiumData(Data): + """Risk Premium Data.""" + + country: str = Field(description="Market country.") + continent: Optional[str] = Field(description="Continent of the country.") + total_equity_risk_premium: PositiveFloat = Field( + description="The total equity risk premium for the country." + ) + country_risk_premium: NonNegativeFloat = Field( + description="The country-specific risk premium." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/sec_filings.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/sec_filings.py new file mode 100644 index 00000000000..1eca448d867 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/sec_filings.py @@ -0,0 +1,138 @@ +"""SEC Filings data model.""" + + +from datetime import datetime +from typing import Literal, Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS + +FORM_TYPES = Literal[ + "1", + "1-A", + "1-E", + "1-K", + "1-N", + "1-SA", + "1-U", + "1-Z", + "10", + "10-D", + "10-K", + "10-M", + "10-Q", + "11-K", + "12b-25", + "13F", + "13H", + "144", + "15", + "15F", + "17-H", + "18", + "18-K", + "19b-4", + "19b-4(e)", + "19b-7", + "2-E", + "20-F", + "24F-2", + "25", + "3", + "4", + "40-F", + "5", + "6-K", + "7-M", + "8-A", + "8-K", + "8-M", + "9-M", + "ABS-15G", + "ABS-EE", + "ABS DD-15E", + "ADV", + "ADV-E", + "ADV-H", + "ADV-NR", + "ADV-W", + "ATS", + "ATS-N", + "ATS-R", + "BD", + "BD-N", + "BDW", + "C", + "CA-1", + "CB", + "CFPORTAL", + "CRS", + "CUSTODY", + "D", + "F-1", + "F-10", + "F-3", + "F-4", + "F-6", + "F-7", + "F-8", + "F-80", + "F-N", + "F-X", + "ID", + "MA", + "MA-I", + "MA-NR", + "MA-W", + "MSD", + "MSDW", + "N-14", + "N-17D-1", + "N-17f-1", + "N-17f-2", + "N-18f-1", + "N-1A", + "N-2", + "N-23c-3", + "N-27D-1", + "N-3", + "N-4", + "N-5", + "N-54A", + "N-54C", + "N-6", + "N-6EI-1", + "N-6F", + "N-8A", + "N-8B-2", + "N-8B-4", + "N-8F", + "N-CEN", +] + + +class SECFilingsQueryParams(QueryParams, BaseSymbol): + """SEC Filings Query.""" + + type: FORM_TYPES = Field( + default="10-K", description="The type of the SEC filing form." + ) + page: Optional[int] = Field(default=0, description="Page number of the results.") + limit: Optional[int] = Field( + default=100, description=QUERY_DESCRIPTIONS.get("limit", "") + ) + + +class SECFilingsData(Data, BaseSymbol): + """SEC Filings Data.""" + + filling_date: datetime = Field(description="The filling date of the SEC filing.") + accepted_date: datetime = Field(description="The accepted date of the SEC filing.") + cik: str = Field(description="The CIK of the SEC filing.") + type: str = Field(description="The type of the SEC filing.") + link: str = Field(description="The link of the SEC filing.") + final_link: str = Field(description="The final link of the SEC filing.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/share_statistics.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/share_statistics.py new file mode 100644 index 00000000000..a174fc52532 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/share_statistics.py @@ -0,0 +1,31 @@ +"""Share Statistics Data Model.""" + + +from datetime import date as dateType + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS + + +class ShareStatisticsQueryParams(QueryParams, BaseSymbol): + """Share Statistics Query.""" + + +class ShareStatisticsData(Data, BaseSymbol): + """Return Share Statistics Data.""" + + date: dateType = Field(description=QUERY_DESCRIPTIONS.get("date", "")) + free_float: float = Field( + description="The percentage of unrestricted shares of a publicly-traded company." + ) + float_shares: float = Field( + description="The number of shares available for trading by the general public." + ) + outstanding_shares: float = Field( + description="The total number of shares of a publicly-traded company." + ) + source: str = Field(description="Source of the received data.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_eod.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_eod.py new file mode 100644 index 00000000000..ead78900695 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_eod.py @@ -0,0 +1,37 @@ +"""Stock aggregate end of day price data model.""" + + +from datetime import date, datetime +from typing import Optional + +from pydantic import Field, PositiveFloat + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + + +class StockEODQueryParams(QueryParams, BaseSymbol): + """Stock end of day Query.""" + + start_date: Optional[date] = Field( + default=None, + description=QUERY_DESCRIPTIONS.get("start_date", ""), + ) + end_date: Optional[date] = Field( + default=None, + description=QUERY_DESCRIPTIONS.get("end_date", ""), + ) + + +class StockEODData(Data): + """Stock end of day price Data.""" + + date: datetime = Field(description=DATA_DESCRIPTIONS.get("date", "")) + open: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("open", "")) + high: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("high", "")) + low: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("low", "")) + close: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("close", "")) + volume: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("volume", "")) + vwap: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("vwap", "")) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_insider_trading.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_insider_trading.py new file mode 100644 index 00000000000..ae531024680 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_insider_trading.py @@ -0,0 +1,86 @@ +"""Stock insider trading data model.""" + + +from datetime import date, datetime +from typing import List, Literal, Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol + +TRANSACTION_TYPES = Literal[ + "A-Award", + "C-Conversion", + "D-Return", + "E-ExpireShort", + "F-InKind", + "G-Gift", + "H-ExpireLong", + "I-Discretionary", + "J-Other", + "L-Small", + "M-Exempt", + "O-OutOfTheMoney", + "P-Purchase", + "S-Sale", + "U-Tender", + "W-Will", + "X-InTheMoney", + "Z-Trust", +] + + +class StockInsiderTradingQueryParams(QueryParams, BaseSymbol): + """Stock Insider Trading Query.""" + + transactionType: Optional[List[TRANSACTION_TYPES]] = Field( + default=["P-Purchase"], description="The type of the transaction." + ) + reportingCik: Optional[int] = Field(description="The CIK of the reporting owner.") + companyCik: Optional[int] = Field(description="The CIK of the company owner.") + page: Optional[int] = Field( + default=0, description="The page number of the data to fetch." + ) + + +class StockInsiderTradingData(Data, BaseSymbol): + """Stock Insider Trading Data.""" + + filing_date: datetime = Field( + description="The filing date of the stock insider trading." + ) + transaction_date: date = Field( + description="The transaction date of the stock insider trading." + ) + reporting_cik: int = Field( + description="The reporting CIK of the stock insider trading." + ) + transaction_type: str = Field( + description="The transaction type of the stock insider trading." + ) + securities_owned: int = Field( + description="The securities owned of the stock insider trading." + ) + company_cik: int = Field( + description="The company CIK of the stock insider trading." + ) + reporting_name: str = Field( + description="The reporting name of the stock insider trading." + ) + type_of_owner: str = Field( + description="The type of owner of the stock insider trading." + ) + acquistion_or_disposition: str = Field( + description="The acquistion or disposition of the stock insider trading." + ) + form_type: str = Field(description="The form type of the stock insider trading.") + securities_transacted: float = Field( + description="The securities transacted of the stock insider trading." + ) + price: float = Field(description="The price of the stock insider trading.") + security_name: str = Field( + description="The security name of the stock insider trading." + ) + link: str = Field(description="The link of the stock insider trading.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_multiples.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_multiples.py new file mode 100644 index 00000000000..ce118fe733f --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_multiples.py @@ -0,0 +1,194 @@ +"""Stock Multiples Data Model.""" + +from typing import Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS + + +class StockMultiplesQueryParams(QueryParams, BaseSymbol): + """Stock Multiples Query.""" + + limit: Optional[int] = Field( + default=100, description=QUERY_DESCRIPTIONS.get("limit", "") + ) + + +class StockMultiplesData(Data): + """Stock Multiples Data.""" + + revenue_per_share_ttm: Optional[float] = Field( + description="Revenue per share calculated as trailing twelve months." + ) + net_income_per_share_ttm: Optional[float] = Field( + description="Net income per share calculated as trailing twelve months." + ) + operating_cash_flow_per_share_ttm: Optional[float] = Field( + description="Operating cash flow per share calculated as trailing twelve months." + ) + free_cash_flow_per_share_ttm: Optional[float] = Field( + description="Free cash flow per share calculated as trailing twelve months." + ) + cash_per_share_ttm: Optional[float] = Field( + description="Cash per share calculated as trailing twelve months." + ) + book_value_per_share_ttm: Optional[float] = Field( + description="Book value per share calculated as trailing twelve months." + ) + tangible_book_value_per_share_ttm: Optional[float] = Field( + description="Tangible book value per share calculated as trailing twelve months." + ) + shareholders_equity_per_share_ttm: Optional[float] = Field( + description="Shareholders equity per share calculated as trailing twelve months." + ) + interest_debt_per_share_ttm: Optional[float] = Field( + description="Interest debt per share calculated as trailing twelve months." + ) + market_cap_ttm: Optional[float] = Field( + description="Market capitalization calculated as trailing twelve months." + ) + enterprise_value_ttm: Optional[float] = Field( + description="Enterprise value calculated as trailing twelve months." + ) + pe_ratio_ttm: Optional[float] = Field( + description="Price-to-earnings ratio (P/E ratio) calculated as trailing twelve months." + ) + price_to_sales_ratio_ttm: Optional[float] = Field( + description="Price-to-sales ratio calculated as trailing twelve months." + ) + pocf_ratio_ttm: Optional[float] = Field( + description="Price-to-operating cash flow ratio calculated as trailing twelve months." + ) + pfcf_ratio_ttm: Optional[float] = Field( + description="Price-to-free cash flow ratio calculated as trailing twelve months." + ) + pb_ratio_ttm: Optional[float] = Field( + description="Price-to-book ratio calculated as trailing twelve months." + ) + ptb_ratio_ttm: Optional[float] = Field( + description="Price-to-tangible book ratio calculated as trailing twelve months." + ) + ev_to_sales_ttm: Optional[float] = Field( + description="Enterprise value-to-sales ratio calculated as trailing twelve months." + ) + enterprise_value_over_ebitda_ttm: Optional[float] = Field( + description="Enterprise value-to-EBITDA ratio calculated as trailing twelve months." + ) + ev_to_operating_cash_flow_ttm: Optional[float] = Field( + description="Enterprise value-to-operating cash flow ratio calculated as trailing twelve months." + ) + ev_to_free_cash_flow_ttm: Optional[float] = Field( + description="Enterprise value-to-free cash flow ratio calculated as trailing twelve months." + ) + earnings_yield_ttm: Optional[float] = Field( + description="Earnings yield calculated as trailing twelve months." + ) + free_cash_flow_yield_ttm: Optional[float] = Field( + description="Free cash flow yield calculated as trailing twelve months." + ) + debt_to_equity_ttm: Optional[float] = Field( + description="Debt-to-equity ratio calculated as trailing twelve months." + ) + debt_to_assets_ttm: Optional[float] = Field( + description="Debt-to-assets ratio calculated as trailing twelve months." + ) + net_debt_to_ebitda_ttm: Optional[float] = Field( + description="Net debt-to-EBITDA ratio calculated as trailing twelve months." + ) + current_ratio_ttm: Optional[float] = Field( + description="Current ratio calculated as trailing twelve months." + ) + interest_coverage_ttm: Optional[float] = Field( + description="Interest coverage calculated as trailing twelve months." + ) + income_quality_ttm: Optional[float] = Field( + description="Income quality calculated as trailing twelve months." + ) + dividend_yield_ttm: Optional[float] = Field( + description="Dividend yield calculated as trailing twelve months." + ) + payout_ratio_ttm: Optional[float] = Field( + description="Payout ratio calculated as trailing twelve months." + ) + sales_general_and_administrative_to_revenue_ttm: Optional[float] = Field( + description="Sales general and administrative expenses-to-revenue ratio calculated as trailing twelve months." + ) + research_and_development_to_revenue_ttm: Optional[float] = Field( + description="Research and development expenses-to-revenue ratio calculated as trailing twelve months." + ) + intangibles_to_total_assets_ttm: Optional[float] = Field( + description="Intangibles-to-total assets ratio calculated as trailing twelve months." + ) + capex_to_operating_cash_flow_ttm: Optional[float] = Field( + description="Capital expenditures-to-operating cash flow ratio calculated as trailing twelve months." + ) + capex_to_revenue_ttm: Optional[float] = Field( + description="Capital expenditures-to-revenue ratio calculated as trailing twelve months." + ) + capex_to_depreciation_ttm: Optional[float] = Field( + description="Capital expenditures-to-depreciation ratio calculated as trailing twelve months." + ) + stock_based_compensation_to_revenue_ttm: Optional[float] = Field( + description="Stock-based compensation-to-revenue ratio calculated as trailing twelve months." + ) + graham_number_ttm: Optional[float] = Field( + description="Graham number calculated as trailing twelve months." + ) + roic_ttm: Optional[float] = Field( + description="Return on invested capital calculated as trailing twelve months." + ) + return_on_tangible_assets_ttm: Optional[float] = Field( + description="Return on tangible assets calculated as trailing twelve months." + ) + graham_net_net_ttm: Optional[float] = Field( + description="Graham net-net working capital calculated as trailing twelve months." + ) + working_capital_ttm: Optional[float] = Field( + description="Working capital calculated as trailing twelve months." + ) + tangible_asset_value_ttm: Optional[float] = Field( + description="Tangible asset value calculated as trailing twelve months." + ) + net_current_asset_value_ttm: Optional[float] = Field( + description="Net current asset value calculated as trailing twelve months." + ) + invested_capital_ttm: Optional[float] = Field( + description="Invested capital calculated as trailing twelve months." + ) + average_receivables_ttm: Optional[float] = Field( + description="Average receivables calculated as trailing twelve months." + ) + average_payables_ttm: Optional[float] = Field( + description="Average payables calculated as trailing twelve months." + ) + average_inventory_ttm: Optional[float] = Field( + description="Average inventory calculated as trailing twelve months." + ) + days_sales_outstanding_ttm: Optional[float] = Field( + description="Days sales outstanding calculated as trailing twelve months." + ) + days_payables_outstanding_ttm: Optional[float] = Field( + description="Days payables outstanding calculated as trailing twelve months." + ) + days_of_inventory_on_hand_ttm: Optional[float] = Field( + description="Days of inventory on hand calculated as trailing twelve months." + ) + receivables_turnover_ttm: Optional[float] = Field( + description="Receivables turnover calculated as trailing twelve months." + ) + payables_turnover_ttm: Optional[float] = Field( + description="Payables turnover calculated as trailing twelve months." + ) + inventory_turnover_ttm: Optional[float] = Field( + description="Inventory turnover calculated as trailing twelve months." + ) + roe_ttm: Optional[float] = Field( + description="Return on equity calculated as trailing twelve months." + ) + capex_per_share_ttm: Optional[float] = Field( + description="Capital expenditures per share calculated as trailing twelve months." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_news.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_news.py new file mode 100644 index 00000000000..080e7f16eff --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_news.py @@ -0,0 +1,36 @@ +"""Stock News Data Model.""" + + +from datetime import date as dateType +from typing import Optional + +from pydantic import Field, NonNegativeInt, validator + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS + + +class StockNewsQueryParams(QueryParams): + """Stock news query.""" + + symbols: str = Field(min_length=1, description=QUERY_DESCRIPTIONS.get("symbol", "")) + page: int = Field( + default=0, description="The page of the stock news to be retrieved." + ) + limit: Optional[NonNegativeInt] = Field( + default=15, description="The number of results to return per page." + ) + + @validator("symbols", pre=True) + def time_validate(cls, v: str): # pylint: disable=E0213 + return v.upper() + + +class StockNewsData(Data): + """Stock News data.""" + + date: dateType = Field(description="The published date of the news.") + title: str = Field(description="The title of the news.") + text: Optional[str] = Field(default=None, description="The text/body of the news.") + url: str = Field(description="The URL of the news.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_ownership.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_ownership.py new file mode 100644 index 00000000000..b2253c3ddf8 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_ownership.py @@ -0,0 +1,212 @@ +"""Stock owner data model.""" + + +from datetime import date as dateType +from typing import Optional + +from pydantic import Field, validator + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + + +def most_recent_quarter(base: dateType = dateType.today()) -> dateType: + """Get the most recent quarter date. + + Parameter + --------- + base : date + The date to get the most recent quarter. + + Returns + ------- + date : date + The most recent quarter date. + """ + + base = min(base, dateType.today()) # This prevents dates from being in the future + exacts = [(3, 31), (6, 30), (9, 30), (12, 31)] + for exact in exacts: + if base.month == exact[0] and base.day == exact[1]: + return base + if base.month < 4: + return dateType(base.year - 1, 12, 31) + if base.month < 7: + return dateType(base.year, 3, 31) + if base.month < 10: + return dateType(base.year, 6, 30) + return dateType(base.year, 9, 30) + + +class StockOwnershipQueryParams(QueryParams, BaseSymbol): + """Stock ownership Query.""" + + date: dateType = Field(description=QUERY_DESCRIPTIONS.get("date", "")) + page: Optional[int] = Field( + default=0, description="The page number of the data to fetch." + ) + + @validator("date", pre=True) + def date_validate(cls, v: str): # pylint: disable=E0213 + # base = datetime.strptime(v, "%Y-%m-%d").date() + return most_recent_quarter(v) + + +class StockOwnershipData(Data): + """Stock Ownership Data.""" + + date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", "")) + cik: int = Field(description="The cik of the stock ownership.") + filing_date: dateType = Field( + alias="filingDate", description="The filing date of the stock ownership." + ) + investor_name: str = Field( + ..., + alias="investorName", + description="The investor name of the stock ownership.", + ) + symbol: str = Field(description="The symbol of the stock ownership.") + security_name: str = Field( + ..., + alias="securityName", + description="The security name of the stock ownership.", + ) + type_of_security: str = Field( + ..., + alias="typeOfSecurity", + description="The type of security of the stock ownership.", + ) + security_cusip: str = Field( + ..., + alias="securityCusip", + description="The security cusip of the stock ownership.", + ) + shares_type: str = Field( + alias="sharesType", description="The shares type of the stock ownership." + ) + put_call_share: str = Field( + ..., + alias="putCallShare", + description="The put call share of the stock ownership.", + ) + investment_discretion: str = Field( + ..., + alias="investmentDiscretion", + description="The investment discretion of the stock ownership.", + ) + industry_title: str = Field( + ..., + alias="industryTitle", + description="The industry title of the stock ownership.", + ) + weight: float = Field(description="The weight of the stock ownership.") + last_weight: float = Field( + alias="lastWeight", description="The last weight of the stock ownership." + ) + change_in_weight: float = Field( + ..., + alias="changeInWeight", + description="The change in weight of the stock ownership.", + ) + change_in_weight_percentage: float = Field( + ..., + alias="changeInWeightPercentage", + description="The change in weight percentage of the stock ownership.", + ) + market_value: int = Field( + alias="marketValue", description="The market value of the stock ownership." + ) + last_market_value: int = Field( + ..., + alias="lastMarketValue", + description="The last market value of the stock ownership.", + ) + change_in_market_value: int = Field( + ..., + alias="changeInMarketValue", + description="The change in market value of the stock ownership.", + ) + change_in_market_value_percentage: float = Field( + ..., + alias="changeInMarketValuePercentage", + description="The change in market value percentage of the stock ownership.", + ) + shares_number: int = Field( + ..., + alias="sharesNumber", + description="The shares number of the stock ownership.", + ) + last_shares_number: int = Field( + ..., + alias="lastSharesNumber", + description="The last shares number of the stock ownership.", + ) + change_in_shares_number: float = Field( + ..., + alias="changeInSharesNumber", + description="The change in shares number of the stock ownership.", + ) + change_in_shares_number_percentage: float = Field( + ..., + alias="changeInSharesNumberPercentage", + description="The change in shares number percentage of the stock ownership.", + ) + quarter_end_price: float = Field( + ..., + alias="quarterEndPrice", + description="The quarter end price of the stock ownership.", + ) + avg_price_paid: float = Field( + ..., + alias="avgPricePaid", + description="The average price paid of the stock ownership.", + ) + is_new: bool = Field(alias="isNew", description="Is the stock ownership new.") + is_sold_out: bool = Field( + alias="isSoldOut", description="Is the stock ownership sold out." + ) + ownership: float = Field(description="How much is the ownership.") + last_ownership: float = Field( + alias="lastOwnership", description="The last ownership amount." + ) + change_in_ownership: float = Field( + alias="changeInOwnership", description="The change in ownership amount." + ) + change_in_ownership_percentage: float = Field( + ..., + alias="changeInOwnershipPercentage", + description="The change in ownership percentage.", + ) + holding_period: int = Field( + ..., + alias="holdingPeriod", + description="The holding period of the stock ownership.", + ) + first_added: dateType = Field( + ..., + alias="firstAdded", + description="The first added date of the stock ownership.", + ) + performance: float = Field(description="The performance of the stock ownership.") + performance_percentage: float = Field( + ..., + alias="performancePercentage", + description="The performance percentage of the stock ownership.", + ) + last_performance: float = Field( + ..., + alias="lastPerformance", + description="The last performance of the stock ownership.", + ) + change_in_performance: float = Field( + ..., + alias="changeInPerformance", + description="The change in performance of the stock ownership.", + ) + is_counted_for_performance: bool = Field( + ..., + alias="isCountedForPerformance", + description="Is the stock ownership counted for performance.", + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_peers.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_peers.py new file mode 100644 index 00000000000..599364aa24f --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_peers.py @@ -0,0 +1,23 @@ +"""Stock Peers data model.""" + +from typing import List, Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.standard_models.base import BaseSymbol +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS + + +class StockPeersQueryParams(QueryParams, BaseSymbol): + """Stock Peers query model.""" + + +class StockPeersData(Data): + """Stock Peers data.""" + + symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", "")) + peers_list: Optional[List[str]] = Field( + description="A list of stock peers based on sector, exchange and market cap." + ) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_splits.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_splits.py new file mode 100644 index 00000000000..624e3b0802a --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/stock_splits.py @@ -0,0 +1,32 @@ +"""Stock Split Calendar data model.""" + + +from datetime import date as dateType +from typing import Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS + + +class StockSplitCalendarQueryParams(QueryParams): + """Stock Split Calendar query model.""" + + start_date: Optional[dateType] = Field( + description=QUERY_DESCRIPTIONS.get("start_date", ""), default=None + ) + end_date: Optional[dateType] = Field( + description=QUERY_DESCRIPTIONS.get("end_date", ""), default=None + ) + + +class StockSplitCalendarData(Data): + """Stock Split Calendar data.""" + + date: dateType = Field(description="The date of the stock splits.") + label: str = Field(description="The label of the stock splits.") + symbol: str = Field(description="The symbol of the company.") + numerator: float = Field(description="The numerator of the stock splits.") + denominator: float = Field(description="The denominator of the stock splits.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/treasury_rates.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/treasury_rates.py new file mode 100644 index 00000000000..96c87889bcd --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/treasury_rates.py @@ -0,0 +1,42 @@ +"""Treasury Rates Data Model.""" + + +from datetime import date as dateType +from typing import Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data +from openbb_provider.abstract.query_params import QueryParams +from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS + + +class TreasuryRatesQueryParams(QueryParams): + """Treasury Rates Query.""" + + start_date: Optional[dateType] = Field( + default=None, + description=QUERY_DESCRIPTIONS.get("start_date", ""), + ) + end_date: Optional[dateType] = Field( + default=None, + description=QUERY_DESCRIPTIONS.get("end_date", ""), + ) + + +class TreasuryRatesData(Data): + """Return Treasury Rates Data.""" + + date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", "")) + month_1: float = Field(description="The 1 month treasury rate.") + month_2: float = Field(description="The 2 month treasury rate.") + month_3: float = Field(description="The 3 month treasury rate.") + month_6: float = Field(description="The 6 month treasury rate.") + year_1: float = Field(description="The 1 year treasury rate.") + year_2: float = Field(description="The 2 year treasury rate.") + year_3: float = Field(description="The 3 year treasury rate.") + year_5: float = Field(description="The 5 year treasury rate.") + year_7: float = Field(description="The 7 year treasury rate.") + year_10: float = Field(description="The 10 year treasury rate.") + year_20: float = Field(description="The 20 year treasury rate.") + year_30: float = Field(description="The 30 year treasury rate.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/utils/__init__.py b/openbb_sdk/sdk/provider/openbb_provider/utils/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/sdk/provider/openbb_provider/utils/descriptions.py b/openbb_sdk/sdk/provider/openbb_provider/utils/descriptions.py new file mode 100644 index 00000000000..7e2c9889b9c --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/utils/descriptions.py @@ -0,0 +1,27 @@ +"""Common descriptions for model fields.""" + +QUERY_DESCRIPTIONS = { + "symbol": "Symbol to get data for.", + "start_date": "Start date of the data, in YYYY-MM-DD format.", + "end_date": "End date of the data, in YYYY-MM-DD format.", + "weekly": "Whether to return weekly data.", + "monthly": "Whether to return monthly data.", + "period": "Period of the data to return (quarterly or annually).", + "date": "A specific date to get data for.", + "limit": "The number of data entries to return.", + "countries": "The country or countries to get data.", + "units": "The data units.", + "frequency": "The data time frequency.", +} + +DATA_DESCRIPTIONS = { + "symbol": "Symbol representing the entity requested in the data.", + "date": "The date of the data.", + "open": "The open price of the symbol.", + "high": "The high price of the symbol.", + "low": "The low price of the symbol.", + "close": "The close price of the symbol.", + "volume": "The volume of the symbol.", + "adj_close": "The adjusted close price of the symbol.", + "vwap": "Volume Weighted Average Price of the symbol.", +} diff --git a/openbb_sdk/sdk/provider/openbb_provider/helpers.py b/openbb_sdk/sdk/provider/openbb_provider/utils/helpers.py similarity index 54% rename from openbb_sdk/sdk/provider/openbb_provider/helpers.py rename to openbb_sdk/sdk/provider/openbb_provider/utils/helpers.py index 377c7e10497..20993df72ce 100644 --- a/openbb_sdk/sdk/provider/openbb_provider/helpers.py +++ b/openbb_sdk/sdk/provider/openbb_provider/utils/helpers.py @@ -1,11 +1,12 @@ """Provider helpers.""" -import json +import random import re -from io import StringIO from typing import Callable, Dict, List, Optional, Type, Union +import requests + from openbb_provider.abstract.fetcher import DataType, ProviderDataType @@ -32,15 +33,6 @@ def get_querystring(items: dict, exclude: List[str]) -> str: return "&".join([f"{k}={v}" for k, v in params.items()]) -def extract_data(schema, data: list): - new_data: Dict[str, list] = {x: [] for x in schema.__fields__} - for item in data: - for key, value in item.items(): - if key in new_data: - new_data[key].append(value) - return schema(**new_data) - - def process( row: ProviderDataType, key: str, processors: Optional[Dict[str, Callable]] = None ): @@ -120,32 +112,71 @@ def data_transformer( return new_data -class BasicResponse: - def __init__(self, response: StringIO): - # Find a way to get the status code - self.status_code = 200 - response.seek(0) - self.text = response.read() +def get_user_agent() -> str: + """Get a not very random user agent.""" + user_agent_strings = [ + "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.10; rv:86.1) Gecko/20100101 Firefox/86.1", + "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:86.1) Gecko/20100101 Firefox/86.1", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:82.1) Gecko/20100101 Firefox/82.1", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:86.0) Gecko/20100101 Firefox/86.0", + "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:86.0) Gecko/20100101 Firefox/86.0", + "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.10; rv:83.0) Gecko/20100101 Firefox/83.0", + "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:84.0) Gecko/20100101 Firefox/84.0", + ] - def json(self) -> dict: - return json.loads(self.text) + return random.choice(user_agent_strings) # nosec -def request(url: str) -> BasicResponse: - """ - Request function for PyScript. Pass in Method and make sure to await! - Parameters: - ----------- - url: str - URL to make request to +def make_request( + url: str, method: str = "GET", timeout: int = 10, **kwargs +) -> requests.Response: + """Abstract helper to make requests from a url with potential headers and params. - Return: + Parameters + ---------- + url : str + Url to make the request to + method : str, optional + HTTP method to use. Can be "GET" or "POST", by default "GET" + timeout : int, optional + Timeout in seconds, by default 10. Can be overwritten by user setting, request_timeout + + Returns ------- - response: BasicRequest - BasicRequest object with status_code and text attributes - """ - # pylint: disable=import-outside-toplevel - from pyodide.http import open_url + requests.Response + Request response object - response = open_url(url) - return BasicResponse(response) + Raises + ------ + ValueError + If invalid method is passed + """ + # We want to add a user agent to the request, so check if there are any headers + # If there are headers, check if there is a user agent, if not add one. + # Some requests seem to work only with a specific user agent, so we want to be able to override it. + headers = kwargs.pop("headers", {}) + preferences = kwargs.pop("preferences", None) + if preferences and "request_timeout" in preferences: + timeout = preferences["request_timeout"] or timeout + + if "User-Agent" not in headers: + headers["User-Agent"] = get_user_agent() + + # Allow a custom session for caching, if desired + _session = kwargs.pop("session", None) or requests + + if method.upper() == "GET": + return _session.get( + url, + headers=headers, + timeout=timeout, + **kwargs, + ) + if method.upper() == "POST": + return _session.post( + url, + headers=headers, + timeout=timeout, + **kwargs, + ) + raise ValueError("Method must be GET or POST") diff --git a/openbb_sdk/sdk/provider/tests/.gitkeep b/openbb_sdk/sdk/provider/tests/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_sdk/tests/conftest.py b/openbb_sdk/tests/conftest.py new file mode 100644 index 00000000000..9e6ec5e1b07 --- /dev/null +++ b/openbb_sdk/tests/conftest.py @@ -0,0 +1,2 @@ +# TODO: See if we can dynamically import the test modules under the openbb_sdk +# file structure. diff --git a/openbb_terminal/account/account_controller.py b/openbb_terminal/account/account_controller.py index 459675080ec..12b26bb8869 100644 --- a/openbb_terminal/account/account_controller.py +++ b/openbb_terminal/account/account_controller.py @@ -2,6 +2,7 @@ import argparse import logging from pathlib import Path from typing import Dict, List, Optional +from uuid import UUID from openbb_terminal.account.account_view import ( display_default_routines, @@ -55,12 +56,12 @@ class AccountController(BaseController): """Constructor""" super().__init__(queue) self.LOCAL_ROUTINES: Dict[str, Path] = {} - self.REMOTE_CHOICES: List[str] = [] + self.REMOTE_CHOICES: Dict[str, UUID] = {} self.DEFAULT_ROUTINES: List[Dict[str, str]] = self.fetch_default_routines() - self.DEFAULT_CHOICES: List[str] = [ - r["name"] for r in self.DEFAULT_ROUTINES if "name" in r - ] + self.DEFAULT_CHOICES: Dict[str, None] = { + r["name"]: None for r in self.DEFAULT_ROUTINES if "name" in r + } if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: self.choices: dict = self.choices_default @@ -75,7 +76,11 @@ class AccountController(BaseController): {c: {} for c in self.LOCAL_ROUTINES} ) self.choices["download"]["--name"].update( - {c: {} for c in self.DEFAULT_CHOICES + self.REMOTE_CHOICES} + { + c: {} + for c in list(self.DEFAULT_CHOICES.keys()) + + list(self.REMOTE_CHOICES.keys()) + } ) self.choices["delete"]["--name"].update( {c: {} for c in self.REMOTE_CHOICES} @@ -253,7 +258,8 @@ class AccountController(BaseController): ) df, page, pages = get_personal_routines_info(response) if not df.empty: - self.REMOTE_CHOICES += list(df["name"]) + temp_dict = dict(zip(df["name"], df["uuid"])) + self.REMOTE_CHOICES = {**self.REMOTE_CHOICES, **temp_dict} self.update_runtime_choices() display_personal_routines(df, page, pages) else: @@ -366,7 +372,8 @@ class AccountController(BaseController): console.print("[info]Aborted.[/info]") if response and response.status_code == 200: - self.REMOTE_CHOICES.append(name) + the_uuid = response.json()["uuid"] + self.REMOTE_CHOICES[name] = the_uuid self.update_runtime_choices() # store data in list with "personal/default" to identify data's routine type @@ -401,12 +408,13 @@ class AccountController(BaseController): data = [] name = " ".join(ns_parser.name) # Personal routines - response = Hub.download_routine( - auth_header=get_current_user().profile.get_auth_header(), - name=name, - ) - if response and response.status_code == 200: - data = [response.json(), "personal"] + if name in self.REMOTE_CHOICES: + response = Hub.download_routine( + auth_header=get_current_user().profile.get_auth_header(), + uuid=self.REMOTE_CHOICES[name], + ) + if response and response.status_code == 200: + data = [response.json(), "personal"] # Default routine elif name in self.DEFAULT_CHOICES: data = [ @@ -490,14 +498,14 @@ class AccountController(BaseController): if i.lower() in ["y", "yes"]: response = Hub.delete_routine( auth_header=get_current_user().profile.get_auth_header(), - name=name, + uuid=self.REMOTE_CHOICES[name], ) if ( response and response.status_code == 200 and name in self.REMOTE_CHOICES ): - self.REMOTE_CHOICES.remove(name) + self.REMOTE_CHOICES.pop(name) self.update_runtime_choices() else: console.print("[info]Aborted.[/info]") diff --git a/openbb_terminal/account/account_view.py b/openbb_terminal/account/account_view.py index 45f2f93a4e2..5dcbff376bf 100644 --- a/openbb_terminal/account/account_view.py +++ b/openbb_terminal/account/account_view.py @@ -4,6 +4,34 @@ from openbb_terminal.helper_funcs import print_rich_table from openbb_terminal.rich_config import console +def clean_df(df: pd.DataFrame) -> pd.DataFrame: + """ + Cleans the dataframe before displaying it. + + Parameters + ---------- + df : pd.DataFrame + The dataframe to clean. + + Returns + ------- + pd.DataFrame + The cleaned dataframe. + """ + df["updated_date"] = pd.to_datetime(df["updated_date"]) + df["updated_date"] = df["updated_date"].dt.strftime("%Y-%m-%d %H:%M:%S") + df.replace(to_replace=[None], value="-", inplace=True) + to_rename = { + "name": "Name", + "description": "Description", + "version": "Version", + "updated_date": "Last Update", + } + df = df.rename(columns=to_rename) + df = df[["Name", "Description", "Version", "Last Update"]] + return df + + def display_personal_routines(df: pd.DataFrame, page: int, pages: int): """Display the routines. @@ -20,18 +48,17 @@ def display_personal_routines(df: pd.DataFrame, page: int, pages: int): title = f"Personal routines - page {page}" if pages: title += f" of {pages}" + df = clean_df(df) - df["updated_date"] = pd.to_datetime(df["updated_date"]) - df["updated_date"] = df["updated_date"].dt.strftime("%Y-%m-%d %H:%M:%S") - df.replace(to_replace=[None], value="-", inplace=True) print_rich_table( df=df, title=title, - headers=["Name", "Description", "Version", "Last update"], + headers=list(df.columns), show_index=True, index_name="#", ) - except Exception: + except Exception as exc: + print(exc) console.print("Failed to display personal routines.") @@ -44,9 +71,9 @@ def display_default_routines(df: pd.DataFrame): The default routines list. """ try: - df["date_updated"] = pd.to_datetime(df["date_updated"]) - df["date_updated"] = df["date_updated"].dt.strftime("%Y-%m-%d %H:%M:%S") - df.replace(to_replace=[None], value="-", inplace=True) + df = df.rename(columns={"date_updated": "updated_date"}) + print(df) + df = clean_df(df) print_rich_table( df=df, title="Default routines", diff --git a/openbb_terminal/common/quantitative_analysis/qa_view.py b/openbb_terminal/common/quantitative_analysis/qa_view.py index c273a12ba58..83c26897eab 100644 --- a/openbb_terminal/common/quantitative_analysis/qa_view.py +++ b/openbb_terminal/common/quantitative_analysis/qa_view.py @@ -121,7 +121,8 @@ def display_hist( fig.update_layout( xaxis_title="Value", - yaxis_title="Proportion", + margin=dict(r=40), + yaxis=dict(title="Proportion", title_standoff=40), bargap=0.01, bargroupgap=0, ) diff --git a/openbb_terminal/core/plots/no_import.py b/openbb_terminal/core/plots/no_import.py index eb3fa1f1029..3d9a22d084c 100644 --- a/openbb_terminal/core/plots/no_import.py +++ b/openbb_terminal/core/plots/no_import.py @@ -72,11 +72,11 @@ class DummyBackend: and get_current_system().LOGGING_SUB_APP != "sdk" ): console.print(pywry_missing) - if console.input( - "If you prefer to continue without interactive plots/tables, " - "press [green]enter[/] or [red]ctrl+c[/] to exit." - ): - dotenv.set_key(SETTINGS_ENV_FILE, "PLOT_ENABLE_PYWRY", "0") + console.print( + "\n\nContinuing with default plotly behaviour (open in browser).", + style="yellow", + ) + dotenv.set_key(SETTINGS_ENV_FILE, "PLOT_ENABLE_PYWRY", "0") current_user.preferences.USE_INTERACTIVE_DF = False set_current_user(current_user) diff --git a/openbb_terminal/core/plots/plotly_helper.py b/openbb_terminal/core/plots/plotly_helper.py index 68fb219d311..dfa7f92aef0 100644 --- a/openbb_terminal/core/plots/plotly_helper.py +++ b/openbb_terminal/core/plots/plotly_helper.py @@ -3,7 +3,7 @@ import json import sys import textwrap -from datetime import datetime +from datetime import datetime, timedelta from math import floor from pathlib import Path from typing import ( @@ -598,9 +598,9 @@ class OpenBBFigure(go.Figure): colors = [None] * len(valid_x) # type: ignore max_y = 0 - for i, (x_i, name_i, color_i) in enumerate(zip(valid_x, name, colors)): + for i0, (x_i, name_i, color_i) in enumerate(zip(valid_x, name, colors)): if not color_i: - color_i = theme.up_color if i % 2 == 0 else theme.down_color + color_i = theme.up_color if i0 % 2 == 0 else theme.down_color res_mean, res_std = np.mean(x_i), np.std(x_i) res_min, res_max = min(x_i), max(x_i) @@ -639,13 +639,13 @@ class OpenBBFigure(go.Figure): if show_rug: self.add_scatter( x=x_i, - y=[0.002] * len(x_i), + y=[0.00002] * len(x_i), name=name_i if len(name) < 2 else name[1], mode="markers", marker=dict( color=theme.down_color, symbol="line-ns-open", - size=8, + size=10, ), row=row, col=col, @@ -1340,6 +1340,7 @@ class OpenBBFigure(go.Figure): return # We get the missing days + is_daily = df_data.index[-1].time() == df_data.index[-2].time() dt_days = pd.date_range(start=dt_start, end=dt_end, normalize=True) # We get the dates that are missing @@ -1351,17 +1352,22 @@ class OpenBBFigure(go.Figure): if len(dt_missing_days) < 2_000: rangebreaks = [dict(values=dt_missing_days)] - # We get the frequency of the data to hide intra-day gaps - if df_data.index[-1].time() != df_data.index[-2].time(): - freq = df_data.index[1] - df_data.index[0] - freq_mins = int(freq.seconds / 60) - break_values = ( - df_data.resample(f"{freq_mins}T") - .max() - .index.union(df_data.index) - .difference(df_data.index) - ) - rangebreaks = [dict(values=break_values, dvalue=freq_mins * 60 * 1000)] + df_data = df_data.sort_index() + # We add a rangebreak if the first and second time are not the same + # since daily data will have the same time (00:00) + if not is_daily: + for i in range(len(df_data) - 1): + if df_data.index[i + 1] - df_data.index[i] > timedelta(hours=2): + rangebreaks.insert( + 0, + dict( + bounds=[ + df_data.index[i] + + timedelta(minutes=60 - df_data.index[i].minute), + df_data.index[i + 1], + ] + ), + ) self.update_xaxes(rangebreaks=rangebreaks, row=row, col=col) @@ -1372,16 +1378,19 @@ class OpenBBFigure(go.Figure): for row, row_dict in self._subplot_xdates.items(): for col, values in row_dict.items(): - x_values = ( - pd.to_datetime(np.concatenate(values)) - .to_pydatetime() - .astype("datetime64[ms]") - ) - self.hide_date_gaps( - pd.DataFrame(index=x_values.tolist()), - row=row, - col=col, - ) + try: + x_values = ( + pd.to_datetime(np.concatenate(values)) + .to_pydatetime() + .astype("datetime64[ms]") + ) + self.hide_date_gaps( + pd.DataFrame(index=x_values.tolist()), + row=row, + col=col, + ) + except ValueError: + continue def to_subplot( self, diff --git a/openbb_terminal/core/plots/table.html b/openbb_terminal/core/plots/table.html index 98161f92834..93a59444d99 100644 --- a/openbb_terminal/core/plots/table.html +++ b/openbb_terminal/core/plots/table.html @@ -79,7 +79,7 @@ }