* [Feature] OpenBB V5: Initial `openbb-core` Refactor (#7482)
* initial refactor
* ruff does not dictate style
* unresolved imports in test action
* fix general linting
* resolve pandas imports for actions run
* httpx in test workflow
* pytest-asyncio in test workflow
* pandas.Index(...)
* max version for windows CI is 3.14
* remove temp refactor files that shouldn't have been committed:
* use ruff format instead of black
* formatting and typing
* tab space helper
* test fixtures..
* ty check
* also run integration test in CI
* remove that annoying labeler action
* [V5] `openbb-core`: 100% Test Coverage (#7484)
* increase test coverage to exercise 100% of the testable code path in openbb-core
* model_fields syntax cleanup
* set the environment variable for CI runs
* run codeql on v5 branch
* more CI env setup
* cleanup redundant files
* ruff
* dedupe workflow trigger
* some windows coverage missing
* ubuntu integration test job
* more windows coverage
* test_reference_generator
* test_docstring_generator
* remove line no references from test function docstrings
* clean useless comments from test files
* fix polars type check
* fix test_obbject
* fix test_obbject
* package builder fixes
* charting pyproject version
* fix docstring generator for nested models
* missing coverage
* [V5] `openbb-core`: Add Backwards-Compatible Layered Config System (openbb.toml) (#7492)
* add backwards-compatible layered config system
* fix path reference in windows test
* need python version-aware handling in docstring generator
* update for deprecated pydantic syntax
* ruff UP007
* test_system_settings
* remove v4 platform workflow
* [V5] Refactor `openbb-oecd` for full SDMX 2 API Exposure and V5 Packaging (#7413)
* stash some changes
* add more robust testing
* mypy
* point PR at V5
* introduce spec file
* codespell
* test fix
* fix workflow environment setup
* fix workflow environment setup
* fix workflow environment setup
* add pyyaml to dependencies
* split lint jobs
* fix workflow environment setup
* fix workflow environment setup
* workflow env setup
* workflow env setup
* clean up code comments
* add auth hook entrypoints
* codespell
* add codegen feature
* codespell
* move _unpack into dispatchers for consistency with codegen packages
* surface nested models in the response
* fix missing coverage in CI
* socrata updates
* test fix
* detect plotly output
* add --include and --exclude flags from generate-extension command
* refactor openbb-platform-api for V5 operations and spec file handling
* delete poetry.lock
* add test workflow
* add temp branch id for test workflow
* add httpx to dev dependencies
* add a conftest.py
* tweak conftest
* tweak conftest
* python 3.10 issues
* more python 3.10
* more python 3.10
* more python 3.10
* more python 3.10
* fix url template resolution
* multi-spec support
* fix a windows path issue
* merge branch develop
* table builder, integration tests, and more
* linting
* merge branch develop
* bit of cleanup
* some more updates, cleanup, and apps.json
* codespell
* black
* integration test params and linting
* mypy
* pylint
* black
* more pylint
* recapture teest cassettes
* black again
* more linting
* table presentation choices
* black
* fix merge conflict
* split up metadata module and reformat cache to use msgpack
* mypy
* regenerate lock file
* use json and include dependencies to make openbb-oecd a standalone install
* pylint
* more linting
* serve metadata as dependency instead of assign and initialize
* fix integration_tests_testers to not count dependency injections as paramters
* add assets to git ignore
* ruff
* v5 refactor and publish workflow
* test coverage and ty fixes
* codespell
* update lock file
* workflow setup
* ty check
* workflow setup
* Scope PR to OECD changes only
Restore non-OECD files in the diff against v5 to their v5 state. The
branch was originally created off feature/cli-2 and inherited 186 files
of unrelated changes (cli, agent_server, mcp_server, platform_api,
core/, .github/workflows/* not oecd). Those have nothing to do with the
openbb-oecd v5 refactor and were polluting the PR diff.
* scope cleanup: include leaked v5-resident files (general-linting workflow, platform_api test fixtures)
---------
Co-authored-by: deeleeramone <>
Co-authored-by: Copilot <copilot@github.com>
* fix docstring generator and body param unwrapping
* project ty.toml file
* unpack POST parameters from params: Model
* remove comment chatter
* [V5] `openbb-oecd`: Rename `oecd_utils` as `oecd` (#7509)
* rename oecd_utils to oecd and populate all models there when openbb-economy is not installed
* linting
* bump dependencies
* fix test drift
* linter
* FieldInfo import
* [V5] `openbb-imf`: Refactor for V5 (#7508)
* refactor imf extension for V5
* codespell
* fix some python 3.10 items
* return self on copy of metadata object
* [V5] `openbb-news`: Refactor News for V5 and Add Configurable RSS Endpoint (#7507)
* refactor news for v5 and add rss endpoint
* remove poetry.lock
---------
Co-authored-by: deeleeramone <>
* [V5] `openbb-quantitative`: Refactor for V5 (#7506)
* refactor quantitative for v5
* add factors endpoint and add a few charts
---------
Co-authored-by: deeleeramone <>
* [V5] `openbb-technical`: Refactor for V5 & Add More Quick Win Endpoints (#7502)
* refactor openbb-technical for v5
* forward refs
* linting
* no python 3.15 in test matrix
* reame
* ty
* move all params to body
---------
Co-authored-by: deeleeramone <>
* remove stockgrid provider (#7486)
* [V5] remove wsj provider and related code (#7485)
* remove wsj provider and related code
* purge from metadata
* actions
* actions
* openbb-core publish workflow
* delete biztoc (#7523)
* [V5] Remove `openbb-alpha-vantage` (#7522)
* delete alpha vantage
* more files
---------
Co-authored-by: Theodore Aptekarev <aptekarev@gmail.com>
* [V5] Remove `openbb-benzinga` (#7521)
* delete benzinga
* more references
---------
Co-authored-by: Theodore Aptekarev <aptekarev@gmail.com>
* [V5] `openbb-bls`: Refactor for V5 + Add Many Endpoints (#7518)
* refactor bls for v5, add 100 widgets
* some CI fixes
* no python 3.15
* fix missing dev group in pyproject.toml
* more pyproject fallout
* action
* refactor econometrics for v5 (#7505)
Co-authored-by: deeleeramone <>
* [V5] `openbb-famafrench`: Refactor for V5 (#7503)
* refactor famafrench for v5
* codespell
* finishing touches
---------
Co-authored-by: deeleeramone <>
* [V5] CLI 2.0 - Pluggable Backends, Non-TTY Default, HTTP Dispatcher, Spec Files + Codegen (#7433)
* stash some changes
* add more robust testing
* mypy
* point PR at V5
* introduce spec file
* codespell
* test fix
* fix workflow environment setup
* fix workflow environment setup
* fix workflow environment setup
* add pyyaml to dependencies
* split lint jobs
* fix workflow environment setup
* fix workflow environment setup
* workflow env setup
* workflow env setup
* clean up code comments
* add auth hook entrypoints
* codespell
* add codegen feature
* codespell
* move _unpack into dispatchers for consistency with codegen packages
* surface nested models in the response
* fix missing coverage in CI
* socrata updates
* test fix
* detect plotly output
* add --include and --exclude flags from generate-extension command
* cap test matrix at python 3.14
* no useless comments
* platform controller command description split
* merge URL overloads from path params
* exclude none and unset from model dump
---------
Co-authored-by: deeleeramone <>
Co-authored-by: Copilot <copilot@github.com>
* general-linting
* downgrade aiohttp in devtools for vcrpy
* some ty items and add requests-types stubs for ty in devtools
* markdown linting
* remove integration tests from extensions that were moved into the famafrench provider
* [V5] `openbb-congress-gov`: Refactor for V5 + Convert To Using Bulk Files Instead of `api.congress.gov`. (#7528)
* refactor congress-gov for v5 and use bulk files instead of API with credentials
* integration tests
* apps tab name
* convert caching to sql
* codespell
* [V5] [BugFix] Fix for Breaking Changes in FastAPI 0.137+ (#7537)
* fix route introspection for fastapi 0.137
* recapture oeccd gdp forecast test cassette
---------
Co-authored-by: deeleeramone <>
* [V5] `openbb-platform-api`: Refactor for V5 Spec, Config, Packaging, Test Coverage (#7494)
* stash some changes
* add more robust testing
* mypy
* point PR at V5
* introduce spec file
* codespell
* test fix
* fix workflow environment setup
* fix workflow environment setup
* fix workflow environment setup
* add pyyaml to dependencies
* split lint jobs
* fix workflow environment setup
* fix workflow environment setup
* workflow env setup
* workflow env setup
* clean up code comments
* add auth hook entrypoints
* codespell
* add codegen feature
* codespell
* move _unpack into dispatchers for consistency with codegen packages
* surface nested models in the response
* fix missing coverage in CI
* socrata updates
* test fix
* detect plotly output
* add --include and --exclude flags from generate-extension command
* refactor openbb-platform-api for V5 operations and spec file handling
* delete poetry.lock
* add test workflow
* add temp branch id for test workflow
* add httpx to dev dependencies
* add a conftest.py
* tweak conftest
* tweak conftest
* python 3.10 issues
* more python 3.10
* more python 3.10
* more python 3.10
* more python 3.10
* fix url template resolution
* multi-spec support
* fix a windows path issue
* cap test matrix at python 3.14
* no 3.15 runners for platform-api
* no useless comments
* platform controller command description split
* merge URL overloads from path params
* exclude none and unset from model dump
* markdown lint
* ignore get/post endpoints that are return SSE
* fix remaining fastapi 0.137 fallout
---------
Co-authored-by: deeleeramone <>
Co-authored-by: Copilot <copilot@github.com>
* add full support for AsyncIterator response type for streaming output (#7530)
* [V5] Refactor Shared Pass Through Router Extensions For Hatchling (#7524)
* refactor shared pass-through routers for hatchling and ruff + ty
* add publish workflows
* [V5] `openbb-charting`: Refactor for V5 and Decouple (#7545)
* refactor charting for v5 patterns and decouple entirely
* coveragerc
* lint and tests
* fix tests
* configure dev group with openbb-technical
* ty
* fix cli test
* remove 3.15 from test matrix - dependencies are unstable
---------
Co-authored-by: deeleeramone <>
* fix merge widgets
* workflow cleanup"
* fixes for ty
* fogot that file
* general lint setup
* consolidate lint job
* remove seeking alpha provider (#7570)
* remove tradier provider (#7573)
* remove tiingo provider (#7574)
* remove intrinio provider (#7575)
Remove the openbb-intrinio provider extension from the repository.
Reason: proprietary vendor with no internal access to any endpoint;
cannot build, develop or maintain.
* remove yfinance provider from repo (#7576)
* fix(cli): resolve modular OpenAPI references (#7586)
Fixes #7585
* [V5] `openbb-cli`: Modular OpenAPI Refs (#7589)
* follow up to #7585
* add another codegen test
* unused ty ignore
* Update generated tests to get python parameter names instead of raw API-provider parameter names. (#7597)
* Update the record-vs-envelope heuristic in the payload processing code to not treat a record that happens to have a list datatype as an entire envelope. (#7598)
* Update class generation code to handle empty data that php-style serialization emits as []. (#7599)
* fix ty check failures exposed by newer version
* add drugs.com and brutalist as news rss feeds (#7602)
Co-authored-by: deeleeramone <>
* codespell
* missing test coverage that appeared in famafrench..
* plotly 7 handling for mapbox namespace change
* remove finviz (#7577)
* remove trading economics provider (#7578)
* remove openbb-multpl from repository (#7610)
* add httpx2 to devtools for starlette test client
* restore test coverage to 100% in openbb_core
* remove fmp provider extension from repository (#7665)
* unpin ruff
* fix charting conftest and missing coverage in 3.10
* add charting coverage
* fix new ty issues
* fix tag grouping in swagger docs
* [V5] `openbb-ecb`: Refactor for V5 and Expand Coverage (#7560)
* refactor openbb-ecb for v5 and expand coverage
* some updates
* tests
* workflow
* remove 3.15 from test matrix
* some more table schema updates
* remove some html chrome on embedded articles
---------
Co-authored-by: deeleeramone <>
* [V5] `openbb-federal-reserve`: Refactor for V5 + FFIEC & Regional Fed Routers (#7561)
* refactor for v5
* add workflows
* fix bhcpr schema
* fix pyproject.toml
* fix test
* codespell
* ty
* fix pyproject
* fix ci environment
* fix ci environment
* convert documents endpoints to use fedinprint service
* codeql fix
* fix ny publications and add ny nowcast
* [V5] `openbb-eia`: Refactor for V5 + Add Full API Implementation (#7584)
* eia refactor for v5
* add test and publish workflows
* data browser state
* fix workflows
* more workflow test items
* temp files in tests
* more fixes
* more browsers
* fix codeql issues
* fix mer state and ensure multi-user capability
* uv lock
* table bridge
* fix electricity browser state
* convert HTML widgets to iFrame
* fix recursion error in workspace mcp
* [V5] `openbb-government-us`: Refactor for V5 + Move `uscongress` (#7604)
* v5 government_us provider
* workflow files
* ty and add xlrd to pyproject
* shrink test cassettes
* dev dependencies
* wheel packaging and more tests
* python 3.10 thing..?
* fix test
* default state for multiselect dropdowns
* replace congress html widgets with iframes
* fallback to congress api for congress before 108
* ensure member passage voting count populates
* [V5] Feature: Add JODI World Oil & Gas Database (#7593)
* add the JODI oil and gas database as a provider/router extension
* lint fixes
* add grouping to apps.json
---------
Co-authored-by: deeleeramone <>
* [V5] `openbb-cftc`: Refactor for V5 + Add OIS, Forward Curve, and FX Trade Analysis (#7615)
* refactor openbb-cftc for v5
* add SHIBOR to fixings
---------
Co-authored-by: deeleeramone <>
* fix for plotly 7 breaking change to schemas
* [V5] `openbb-tmx`: Refactor extension for V5 (#7668)
* refactor tmx provider for v5
* fix tests..
* fix tests stack trace exposure
* fix workflow setup
* [V5] `openbb-nasdaq`: Refactor provider for V5. (#7670)
* refactor nasdaq provider for v5
* add workflows
* ty fix
* [V5] `openbb-fred`: Refactor as V5 extension. (#7669)
* refactor fred extension for v5
* fix markdown MD037 lint error in README
* codespell
* [V5] `openbb-cboe`: Refactor for V5 + Add Payoff Diagrams (#7617)
* refactor cboe for v5
* use zoneinfo instead
* fix test
* some fixes related to the cache access
* fix a couple of tests
* fix the test coverage
* [V5] `openbb-deribit`: Refactor provider for V5 (#7674)
* refactor openbb-deribit for v5
* restructure the optimizer around the published combos data and add integration test suites
* fix markdown lint errors
* remove openbb-econdb provider (#7675)
* codespell
* [V5] `openbb-sec`: Refactor for V5 (#7525)
* refactor openbb-sec for v5
* linting
* ty
* normalize compare_company_facts
* add workflows
* python 3.10 fix
* add more thorough url validation and tests
* ty check
* ty for python 3.10
* reconfigure with apps
* temp pyproject overrride
* pyproject
* pyproject
* pyproject
* codespell
* pyproject
* fix uk schema file links in schema_files
* test coverage
* test_cli
* port #7547 into this pr
* no hard coded prefix
* improve filing rendering capabilities
* fix xml text detection
* make codeql happy
* missed a file in the commit
* test coverage
* codespell
* fix flakey test
* some updates
* management_ownership --> management_profiles
* proxy statement markdown
* ruff
* fix test coverage
* more tests
* test coverage
* fix lint errors
* Add SEC adviser search, profile, and universe routes (#7559)
* Add SEC institutional data fetchers
* Add SEC filing index and Form D routes
* Add SEC investment adviser people data
* Expose SEC adviser and institutional routes
* fix(sec): address adviser review feedback
* refactor(sec): narrow adviser support to IAPD search
* feat(sec): add adviser firm and individual searches
* feat(sec): add adviser profile drill-down
* fix: repair pre-commit configuration
* feat(sec): add adviser universe route
* feat(sec): expose complete Form ADV data
---------
Co-authored-by: Danglewood <85772166+deeleeramone@users.noreply.github.com>
* fix test coverage
---------
Co-authored-by: deeleeramone <>
Co-authored-by: Theodore Aptekarev <aptekarev@gmail.com>
* [V5] `openbb-finra`: Refactor for V5 (#7676)
* refactor finra provider for v5
* add workflows
* fix tests
* fix lint errors
* [V5] Update Repository License to Apache 2.0 (#7677)
* change license to Apache 2.0 for V5
* codespell
* bump devtools and repoint pyproject.toml
* Enterprise Flask Migration Toolkit (#7264)
* feat: Add Flask-to-OpenBB enterprise migration toolkit
- Implement Router.from_flask() for zero-touch legacy API conversion
- Enable 4-hour migration vs 6-month traditional rewrite (80% cost reduction)
- Generate FastAPI endpoints from Flask routes with automatic type validation
- Support enterprise consulting services and custom integration workflows
Demonstrates OpenBB's competitive advantage for institutional client migrations.
Complements existing Workspace applications like Open Portfolio suite.
* chore: Resolve .gitignore conflict and add common ignores
* refactor: implement Phase 1 Flask integration architecture- Move Flask conversion logic to openbb_core.app.utils.flask- Add Flask detection to extension_loader - Integrate with OpenBB's core extension system- Add Flask dependency validation with sys.modules checkingAddresses @deeleeramone's architectural feedback for native OpenBB integration.
* docs: add Flask direct entry point demos- demo_direct_flask_entry_point.py: shows direct Flask instance reference- example_pyproject_flask_entry.toml: demonstrates pyproject.toml configurationSupports new architecture eliminating wrapper extensions.
* Implement Phase 1 Flask adapter per code review feedback
- Minimal working implementation with Flask detection and router creation
- Removes complex code generation logic for focused Phase 1 delivery
- Establishes foundation for Phase 2 route conversion features
* Fix Flask integration: Replace missing FlaskToOpenBBAdapter with WSGIMiddleware
- Remove FlaskToOpenBBAdapter which was not implemented
- Use FastAPI's WSGIMiddleware to mount Flask apps directly
- Update extension_loader to use returned Router object
- Convert utils.py to utils/__init__.py to support flask submodule
- Addresses PR review comment about unhandled AttributeError
* Address PR review feedback: fix Flask imports and remove unused files
- Revert .gitignore to upstream/develop
- Remove AI-generated demo/example files and flask_adapter extension
- Move Flask/werkzeug imports inside conditional blocks to avoid ImportError
- Use lazy imports via __getattr__ in utils/flask/__init__.py
- Add Flask tests with @pytest.mark.skipif markers
* Force APIRouter.include_router to propagate Mount paths instead of dropping them. (#1)
This change ensures that mounted ASGI applications (such as Flask apps wrapped in WSGIMiddleware) are not dropped when `APIRouter.include_router` is called.
Previously, while the initial load in `ExtensionLoader` worked as expected, the `Mount` objects were ignored by FastAPI's default inclusion logic. This caused the mounted elements to be lost during the final application assembly, resulting in 404 errors for the Flask routes.
This fix overrides the inclusion logic to manually propagate `Mount` objects downstream.
* feat(flask): Add metadata layer for OpenAPI documentation
Enhance Flask-to-OpenBB converter with structured docstring parsing:
- Parse Google-style docstrings (summary, description, params)
- Extract parameter descriptions from Args section
- Pass metadata to Router.command() for rich OpenAPI docs
This addresses PR feedback to provide tangible benefit by generating
comprehensive API documentation for migrated Flask routes.
* feat(flask): Implement manual OpenAPI spec generation and attachment
- Add FlaskParam and FlaskRouteInfo TypedDict for type-safe metadata
- Implement OpenAPISpecGenerator for Flask→OpenAPI conversion
- Extract route metadata via FlaskIntrospector
- Attach generated spec to router.openbb_openapi_spec_ext
- Add unit tests (3/3 passing)
Addresses WSGI/mount boundary issue where Router.command metadata
cannot propagate through WSGIMiddleware. Spec attached to router
instance for downstream OpenAPI assembly.
Resolves maintainer feedback on PR #7264
* feat(flask): Implement FlaskMountRegistry for multiple mount support
- Add FlaskMountRegistry class for centralized OpenAPI spec management
- Replace router.openbb_openapi_spec_ext with registry pattern
- Support multiple Flask apps at different mount prefixes
- Automatic path rewriting in aggregated OpenAPI specs
- Add registry test case (4/4 tests passing)
Addresses @deeleeramone's feedback on PR #7264 regarding router
attribute fragility and multiple mount support. Registry provides
lifecycle-independent state management and enables enterprise
scenarios with multiple Flask apps.
Resolves: OpenBB-finance/OpenBB#7264 (partial)
* feat(core): load Flask apps as openbb_core_extension WSGI sub-apps
A Flask app referenced directly by an openbb_core_extension entry point is
mounted via WSGIMiddleware and its routes are introspected into openapi.json
(operationId/widgetId/mcp_tool, parameters, request bodies, security, widget_config
and HTML widgets) so the widget factory and MCP server can discover them.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(core): 100% coverage for flask utils + flask dev dependency
Add branch-coverage tests for the Flask integration package (introspection,
AST analysis, docstrings, OpenAPI generation, registry, loader, detection) and
declare flask as a dev dependency so the suite runs in CI.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(flask): treat f-string returns as HTML responses
* fix(flask): mount via a2wsgi instead of deprecated starlette WSGIMiddleware
- add openbb-core[flask] extra (flask, a2wsgi); a2wsgi resolved through require_optional
- run the WSGI serve test through mount_flask_extensions; drop httpx importorskip
- core CI installs [pandas,flask] and httpx2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(flask): cover failed entry point loads; narrow optional types for ty
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Danglewood <85772166+deeleeramone@users.noreply.github.com>
Co-authored-by: deeleeramone <>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* perf(core): vectorize the date conversion in `basemodel_to_df` (#7622)
* perf(core): vectorize the date conversion in basemodel_to_df
basemodel_to_df ran three per-element Python passes over the date column of
every payload it converted. Series.apply(to_datetime) calls pandas.to_datetime
once per row, and because each call receives a lone scalar with no format hint,
pandas also re-runs its datetime format guesser on every row.
Parse the column in one call instead, and use vectorized comparisons for the
all-midnight test and the reduction to datetime.date.
The element-wise parse is kept as a fallback for one case that the vectorized
path cannot represent: a column mixing UTC offsets, such as a series crossing a
daylight-savings boundary. Parsing per element lets each value keep its own
offset and yields an object column of tz-aware Timestamps, whereas a single
to_datetime call over that column raises "Mixed timezones detected". That
behaviour is covered by test_to_df_daylight_savings and is preserved exactly.
Measured on a payload built through EquityHistoricalData, median of 9 under an
exclusive CPU lock:
250 daily bars 8.92 ms -> 1.46 ms
1,000 daily bars 32.93 ms -> 3.13 ms
5,000 daily bars 162.25 ms -> 12.23 ms
* fix a broken test - unrelated to this PR...
---------
Co-authored-by: Danglewood <85772166+deeleeramone@users.noreply.github.com>
Co-authored-by: deeleeramone <>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix lint errors
* fix oecd lint error
* [V5] `openbb-mcp-server`: Refactor for v5 - Spec, Config, Packaging, Test Coverage (#7495)
* stash some changes
* add more robust testing
* mypy
* point PR at V5
* introduce spec file
* codespell
* test fix
* fix workflow environment setup
* fix workflow environment setup
* fix workflow environment setup
* add pyyaml to dependencies
* split lint jobs
* fix workflow environment setup
* fix workflow environment setup
* workflow env setup
* workflow env setup
* clean up code comments
* add auth hook entrypoints
* codespell
* add codegen feature
* codespell
* move _unpack into dispatchers for consistency with codegen packages
* surface nested models in the response
* fix missing coverage in CI
* socrata updates
* test fix
* detect plotly output
* add --include and --exclude flags from generate-extension command
* refactor mcp server for v5
* test action
* fix tests
* tests
* test fixture name
* revert: drop unrelated provider/README changes from mcp-server refactor
Strip the changes to ``README.md`` and ``openbb_platform/providers/{imf,
tradingeconomics,yfinance}`` that snuck into 4ac3602 (refactor mcp server
for v5). Those files are out of scope for the mcp-server work and were
modified inadvertently — restore them to their pre-4ac3602 state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* tests and workflows
* fix workflow
* tests and workflows
* tests and workflows
* tests and workflows
* tests and workflows
* beartype shim for 3.15
* muck around with beartype for 3.15
* muck around with beartype for 3.15
* no 3.15 runners for mcp-server
* update skill files
* cap test matrix at python 3.14
* merge branch v5
* no useless comments
* platform controller command description split
* merge URL overloads from path params
* exclude none and unset from model dump
* refactor for fastmcp4
* wrap markdown lines that are too long
---------
Co-authored-by: deeleeramone <>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* update pre-commit, noxfile, and uv locks
* fixes for docstring generation
* test coverage
* fix cli renderer helper with figure object JSON
* fix the figure font when rendered
* refactor cookiecutter
* bump cookiecutter packagge version
* fix sec test by adding fixture
* fix desktop environments test
* fix(cli): inherit path-item-level OpenAPI parameters (#7606)
* fix(cli): inherit path-item-level OpenAPI parameters
OpenAPI 3.x lets a Path Item Object declare `parameters` shared by every
operation under that path, with operation-level entries overriding them by
(name, in). The codegen read only `op.get("parameters")`, so inherited
parameters were dropped silently — the generated command kept the URL
template but had nothing registered to fill it.
Real-world impact: the Xero Accounting API declares its required
`xero-tenant-id` header once per path item across all 138 paths. Before this
change `build_command_spec` produced 78 commands and none carried the
header, so every generated command 403s. Nothing warned.
Adds `operation_parameters()`, which merges the two levels and dereferences
both, and routes the pre-computed spec path (`build_command_spec`) and the
interactive path (`build_command_index` -> `build_parser_from_operation`)
through it. Unresolvable refs, non-dict entries, and nameless parameters are
skipped as before. Specs without path-item parameters are unaffected.
* fix(cli): narrow path-item inheritance to path and query parameters
Per review: headers do not translate into a Python interface function, and
outbound headers are transport configuration
(SystemSettings.PythonSettings.http.headers, the CLI's -H) or a security-scheme
concern — not a per-command argument.
Path-item `in: header` and `in: cookie` entries are now dropped rather than
inherited. Operation-level parameters are untouched, so an operation that
declares its own header keeps today's behavior.
Scope is now only what cannot be supplied any other way: `in: path` and
`in: query` parameters declared on the Path Item Object.
* fix(cli): merge allOf compositions in response schemas
Response-schema $refs were already expanded by deref_schema, but a schema
whose top level is an allOf composition was left unflattened. The properties
live inside the members, so a consumer reading them off the composition finds
none. fetcher_gen._data_schema looks for `properties.results`, gets nothing,
and falls through to _unwrap_schema_envelopes, which handles oneOf and anyOf
but not allOf. The generated Data model ends up with no fields.
Codat's listBills response is `allOf: [{properties: {results}}, {properties:
{_links, pageNumber, pageSize, totalResults}}]`, which produced an empty model.
merge_allof collapses a composition of object subschemas into one object
schema: properties unioned with the first declaration winning on conflict,
required unioned, and sibling keywords on the allOf node outranking members.
A composition that is not a plain intersection of object subschemas (a member
carrying its own oneOf/anyOf, or a non-object type) is returned unchanged
rather than flattened into something the spec does not describe. Nested
compositions are merged too, so a $ref inside a merged member still resolves.
Applied to extract_response_schema and extract_response_schemas.
Measured across five specs that use the pattern, counting operations whose
response schema exposes readable properties: 244 of 588 before, 394 after
(Codat Accounting 36 to 88, Codat Banking 0 to 8, Codat Commerce 1 to 11,
Rebilly 62 to 104, Box 145 to 183).
* fix(cli): merge allOf compositions in request body schemas
Same gap as the response side. request_body_parameters needs `type: object`
or `properties` to flatten a body into parameters, and an allOf composition
carries neither, so a POST or PUT generated from one gets no body parameters
at all.
extract_request_body_schema now runs merge_allof after deref_schema, matching
extract_response_schema.
Across the same five specs, counting request bodies that produce parameters:
138 of 206 before, 177 after (Codat Accounting 1 to 26 of its 30 bodies,
Rebilly 50 to 63, Box 87 to 88).
* fix(cli): stop annotation keywords blocking an allOf merge
_mergeable_allof_members required every member's keys to come from a small
whitelist, so any unrecognized keyword disqualified the whole composition.
Codat's PagingInfo carries `definitions` next to its properties, which meant
the live Codat-Accounting.yaml never merged and its response models stayed
empty. The APIs.guru copy of the same API omits that key, which is why the
earlier measurements looked clean.
The check is now a blocklist. A member blocks the merge when it carries its
own combinator (oneOf/anyOf/allOf/not), an unresolved $ref left by a cycle,
a scalar or array constraint (enum/const/items), or a type other than object.
Annotation keywords that do not affect the intersection (title, description,
examples, definitions, additionalProperties, vendor x- keys) no longer block
it, and are dropped from the merged result along with the members.
On the live Codat spec, operations whose response schema exposes readable
properties go from 36 of 127 to 114. Across the five specs measured earlier:
244 of 588 before, 488 after (was 394 under the whitelist).
Corpus check over 2,816 response and request-body extractions in 6 specs:
567 changed, every one attributable to an allOf in the pre-merge output,
0 unexplained. Twilio declares no allOf compositions and is unchanged.
* fix(cli): emit a name once when it is both a parameter and a body property
_signature_params appended request-body properties and then operation
parameters without deduplicating, so a name declared in both places produced
two function parameters with the same name and the generated module raised
SyntaxError.
Codat's push endpoints hit this: /push/bankAccounts/{accountId}/bankTransactions
declares accountId as a path parameter and repeats it in the request body, so
connections.push.bankAccounts.bankTransactions generated `accountId: str` and
`accountId: str = None` in one signature.
The operation parameter wins, since it carries the path placeholder and its own
required flag. The request payload is unchanged: _render_body_block derives body
fields from request_body_schema rather than from this list, so accountId is
still sent in the body.
This predates the path-item and allOf work in this PR. On origin/v5 the same
spec produces the same duplicate; on this branch the Codat spec generates 91
commands with 0 duplicated signatures, down from 1.
* fix(cli): use the safe identifier for keyword-named body properties
_render_body_block already writes `{'from': from_}`, keying the payload by the
wire name and reading a safe identifier, but _signature_params declared the raw
name. A body property named after a Python keyword therefore emitted
`from: str = None` and the generated module raised SyntaxError.
This did not fire before the allOf work in this PR, because a composed request
body produced no body parameters at all. Codat's Transfer body is such a
composition and has a `from` property, so connections.push.transfers started
generating invalid syntax once the body flattened.
_signature_params now emits safe_field_name(name)[0], the same helper
fetcher_gen and pydantic_gen already use. The body/query split in
_render_body_block compares against the safe names too: it previously matched
signature names against wire names, so `from_` was not recognized as a body
field and leaked into the query string as a `from_` parameter.
End to end on Codat-Accounting.yaml, generating the full extension and byte
compiling every emitted module: 136 files, 0 failures. origin/v5 emits 1 file
that does not compile (duplicate argument 'accountId').
Known gap left alone: an operation parameter named after a keyword would still
break, since the query and path lines use the raw name as a local variable.
None of the five specs checked declares one.
* fix(cli): do not treat instance data as schema, and sanitize POST identifiers
Two defects found by generating extensions from real specs and byte compiling
every emitted module.
merge_allof and deref_schema walked every value in a schema dict, including
keywords that hold instance data rather than subschemas. A `default` of
`{"allOf": [{"a": 1}, {"b": 2}]}` was rewritten to `{"type": "object"}`, and a
`$ref` key inside an example was resolved as if it were a reference. Both now
copy `default`, `example`, `examples`, `enum`, `const` and vendor `x-` values
through untouched.
_signature_params emitted operation parameter names verbatim, so a wire name
that is not a Python identifier produced invalid syntax. Rebilly declares an
`Organization-Id` header and a `REB-APIKEY` security header, which emitted
`REB-APIKEY: str = None`. It now uses safe_field_name, matching fetcher_gen,
and _render_body_block keys the query dict by the wire name while reading the
safe identifier so the request is unchanged.
This surfaced through the allOf work rather than being caused by it: a composed
request body used to yield no properties, so `has_body` was false in
package_gen and the command was generated as a GET fetcher, where identifiers
were already sanitized. With bodies flattening, 14 of Codat's 24 POST endpoints
correctly move to the POST path, which had never been exercised by these specs.
Generating the full extension and compiling every module:
Codat-Accounting.yaml v5: 1 failure branch: 0 (136 files)
Rebilly 2.1 v5: 14 failures branch: 1 (97 files)
The one remaining Rebilly failure is `async def 3dsecure(` in the router, which
fails identically on v5. Sanitizing that is a naming decision about the command
surface, so it is left alone here.
Known gap: a non-credential header parameter is still placed in the query
string on the POST path. Pre-existing, and unchanged by this PR.
* fix(cli): resolve signature collisions on the emitted identifier
The previous dedupe compared wire names, but the failure it prevents depends on
the sanitized identifier. Two wire names can collapse onto one (`from` and
`from_`, `Organization-Id` and `Organization_Id`), and operation_parameters
keeps a name declared twice under different `in` locations by design, so both
cases could still emit a duplicate argument.
_signature_params now tracks the identifiers it has emitted and skips anything
that would repeat one. Emission order is unchanged, so existing signatures are
byte-identical; only genuinely colliding names are dropped, and operation
parameters still win over body properties.
Neither collision occurs in Codat-Accounting.yaml or Rebilly 2.1. This closes
the class rather than the instance, since the instances are what this PR has
been finding.
Generation unchanged: Codat 136 files 0 failures, Rebilly 97 files 1 failure
(`async def 3dsecure(`, identical on v5).
* test(cli): cover the instance-data guard, and drop a redundant branch
Mutation checking the new tests (revert each fix, confirm its test fails)
found two problems with the previous commits.
The guard that stops merge_allof and deref_schema from walking into `default`,
`example`, `enum`, `const` and vendor values shipped with no test at all. It is
now covered from both directions: a default holding an `allOf` key keeps its
value, and a `$ref` inside a default stays a literal while a real reference
beside it still resolves.
The body-property loop also carried a redundant check. It tested the raw wire
name against the operation parameter names before testing the sanitized one,
but `operation_param_names` holds both forms and `safe_field_name` is idempotent
on identifiers, so the first test could never fire alone. Reverting it left
every test passing, which is what surfaced it. Removed, and the sanitized name
is now computed once.
All nine mutations are caught by the suite: both data guards, the allOf
blocklist in each direction, the response merge wiring, body/parameter
precedence, the keyword identifier, the body/query split and the
emitted-identifier dedupe.
cli/tests at 1,954 passed against the same 80 environment failures as the base.
Generation unchanged: Codat 136 files 0 failures, Rebilly 97 files 1 failure.
* fix(cli): route required path-item headers/cookies through credential handling
* fix(cli): narrow Popen.stdin/stdout for ty in run_in_obb fixture
---------
Co-authored-by: deeleeramone <>
* fix warnings raised by federal_reserve on some xlsx workbook parsing
* fix publish workflows
* test publish pipeline with openbb-core-v2.0.0rc0 (#7681)
* fix publish workflows with missing tag job, some tests and bump fastapi
* fix python 3.10 test failure in core
* actually fix python 3.10 test failure in core
* deincrement openbb-core version to allow release pattern
* bump core version (#7682)
* remove uv.sources from pyproject.toml files and point to openbb-core-2.0.0 on PyPI
* remove oecd cache asset that wasn't supposed to be commuitted
* remove poetry.lock, update pyproject.tml and uv.lock for openbb-platform-api-v2.0.0 (#7684)
Co-authored-by: deeleeramone <>
* add the missing publish-openbb-platform-api.yml workflow file
* remove poetry.lock, update pyproject.tml and uv.lock for openbb-platform-api-v2.0.0 (#7685)
Co-authored-by: deeleeramone <>
* fix the tag generation after publish
* [Release] `openbb-charting-v4.0.0` (#7686)
* up openbb-charting to 4.0.0 and fill missing test coverage in chart_style
* test coverage and format markdown files
---------
Co-authored-by: deeleeramone <>
* remove scikit-learn from dependencies, update tests, and format markdown files (#7687)
Co-authored-by: deeleeramone <>
* [Release] `openbb-cli-v2.0.0` (#7692)
* fix missing test coverage in openbb_cli
* fix conftest
---------
Co-authored-by: deeleeramone <>
* add missing publish-openbb-cli.yml workflow
* [Release] `openbb-cli-v2.0.0` (#7693)
* fix missing test coverage in openbb_cli
* fix conftest
---------
Co-authored-by: deeleeramone <>
* test fixes for cli...
* [Release] `openbb-cli-v.2.0.0` (#7694)
* fix missing test coverage in openbb_cli
* fix conftest
---------
Co-authored-by: deeleeramone <>
* fix hanging mcp server test
* [Release] `openbb-mcp-server-v2.0.0` (#7695)
Co-authored-by: deeleeramone <>
* update uv.lock (#7696)
Co-authored-by: deeleeramone <>
* release openbb-commodity-v2.0.0 (#7697)
Co-authored-by: deeleeramone <>
* update uv.lock (#7698)
Co-authored-by: deeleeramone <>
* update uv.lock (#7699)
Co-authored-by: deeleeramone <>
* update uv.lock (#7700)
Co-authored-by: deeleeramone <>
* [Release] `openbb-econometrics-v2.0.0` (#7701)
* update uv.lock
* update pyproject.toml
---------
Co-authored-by: deeleeramone <>
* update uv.lock (#7703)
Co-authored-by: deeleeramone <>
* update uv.lock (#7702)
Co-authored-by: deeleeramone <>
* update uv.lock (#7704)
Co-authored-by: deeleeramone <>
* update uv.lock (#7705)
Co-authored-by: deeleeramone <>
* update uv.lock (#7706)
Co-authored-by: deeleeramone <>
* [V5] Fix a Number of V5 Bugs (#7707)
* fix a number of v5 bugs
* fix lint error
* more lint
* fixes for quantitative, technical, charting, famafrench for version bumps
---------
Co-authored-by: deeleeramone <>
* update uv.lock (#7709)
Co-authored-by: deeleeramone <>
* update uv.lock (#7708)
Co-authored-by: deeleeramone <>
* update uv.lock (#7710)
Co-authored-by: deeleeramone <>
* update uv.lock (#7711)
Co-authored-by: deeleeramone <>
* [Release] `openbb-cboe-v2.0.0` (#7713)
Co-authored-by: deeleeramone <>
* update uv.lock (#7714)
Co-authored-by: deeleeramone <>
* update uv.lock (#7717)
Co-authored-by: deeleeramone <>
* [Release] `openbb-ecb-v2.0.0` (#7715)
* update uv.lock
* update uv.lock
---------
Co-authored-by: deeleeramone <>
* update uv.lock (#7718)
Co-authored-by: deeleeramone <>
* update uv.lock (#7719)
Co-authored-by: deeleeramone <>
* update uv.lock (#7720)
Co-authored-by: deeleeramone <>
* add missing publish-openbb-fred.yml to repo
* update uv.lock (#7723)
Co-authored-by: deeleeramone <>
* update uv.lock (#7722)
Co-authored-by: deeleeramone <>
* update uv.lock (#7716)
Co-authored-by: deeleeramone <>
* update uv.lock (#7724)
Co-authored-by: deeleeramone <>
* update uv.lock (#7726)
Co-authored-by: deeleeramone <>
* [Release] `openbb-fred-v2.0.0` (#7725)
* update uv.lock
* fix tests and dependencies
* fix test_fred_fetchers
---------
Co-authored-by: deeleeramone <>
* [Release] `openbb-imf-v3.0.0` (#7727)
* update uv.lock
* fix tests and dependencies
* fix test_fred_fetchers
* bump openbb-imf version to 3.0.0
---------
Co-authored-by: deeleeramone <>
* update uv.lock (#7729)
Co-authored-by: deeleeramone <>
* update uv.lock (#7728)
Co-authored-by: deeleeramone <>
* update uv.lock (#7730)
Co-authored-by: deeleeramone <>
* update uv.lock (#7731)
Co-authored-by: deeleeramone <>
* update uv.lock (#7732)
Co-authored-by: deeleeramone <>
* update uv.lock (#7733)
Co-authored-by: deeleeramone <>
* cookiecutter pyproject and lock
* update pyproject.toml (#7734)
Co-authored-by: deeleeramone <>
* update pyproject.toml (#7735)
Co-authored-by: deeleeramone <>
* update uv.lock (#7736)
Co-authored-by: deeleeramone <>
* fix some sec issues, allegedly (#7737)
Co-authored-by: deeleeramone <>
* update uv.lock (#7738)
Co-authored-by: deeleeramone <>
* update main pyproject.toml
* lint fixes
* update assets (#7739)
Co-authored-by: deeleeramone <>
* [Release] `openbb-v5.0.0` (#7740)
* update assets
* add missing workflow
---------
Co-authored-by: deeleeramone <>
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: deeleeramone <>
Co-authored-by: Theodore Aptekarev <aptekarev@gmail.com>
Co-authored-by: Dex <i@dex.moe>
Co-authored-by: Monte Hoover <monte.b.hoover@gmail.com>
Co-authored-by: Boris Li <65254053+BorisQuanLi@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Lucas Zimmermann <lucaszmayer@gmail.com>
42 KiB
Vendored
Contributing to the OpenBB Platform
- Contributing to the OpenBB Platform
- Introduction
- Dependency Management
- Developer Guidelines
- Contributor Guidelines
Introduction
This document provides guidelines for contributing to the OpenBB Platform. Throughout this document, we will be differentiating between two types of contributors: Developers and Contributors.
- Developers: Those who are building new features or extensions for the OpenBB Platform or leveraging the OpenBB Platform.
- Contributors: Those who contribute to the existing codebase, by opening a Pull Request thus giving back to the community.
Why is this distinction important?
The OpenBB Platform is designed as a foundation for further development. We anticipate a wide range of creative use cases for it. Some use cases may be highly specific or detail-oriented, solving particular problems that may not necessarily fit within the OpenBB Platform Github repository. This is entirely acceptable and even encouraged. This document provides a comprehensive guide on how to build your own extensions, add new data points, and more.
The Developer role, as defined in this document, can be thought of as the foundational role. Developers are those who use the OpenBB Platform as is or build upon it.
Conversely, the Contributor role refers to those who enhance the OpenBB Platform codebase (either by directly adding to the OpenBB Platform or by extending the extension repository). Contributors are willing to go the extra mile, spending additional time on quality assurance, testing, or collaborating with the OpenBB development team to ensure adherence to standards, thereby giving back to the community.
Quick look into the OpenBB Platform
The OpenBB Platform is built by the Open-Source community and is characterized by its core and extensions. The core handles data integration and standardization, while the extensions enable customization and advanced functionalities. The OpenBB Platform is designed to be used both from a Python interface and a REST API.
The REST API is built on top of FastAPI. With openbb-platform-api installed, start it with:
openbb-api
The bare FastAPI app, openbb_core.api.rest_api:app, also runs under any ASGI server:
uvicorn openbb_core.api.rest_api:app --host 127.0.0.1 --port 8000 --reload
The Python interface we provide to users is the openbb Python package, which ships inside openbb-core.
The code you will find in openbb/package is generated from a script and it is just a wrapper around openbb-core and any installed extensions.
When the user runs import openbb, from openbb import obb or other variants, the script that generates the packaged code is triggered. It detects if there are new extensions installed in the environment and rebuilds the packaged code accordingly. If new extensions are not found, it just uses the current packaged version. Set OPENBB_AUTO_BUILD=false to disable this.
When you are developing chances are you want to manually trigger the package rebuild.
You can do that with:
openbb-build
The Python interface can be imported with:
from openbb import obb
This document will take you through two types of contributions:
- Building a custom extension
- Contributing directly to the OpenBB Platform
Before moving forward, please take a look at the high-level view of the OpenBB Platform architecture. We will go over each bit in this document.
What is the Standardization Framework?
The Standardization Framework is a set of tools and guidelines that enable the user to query and obtain data in a consistent way across multiple providers.
Each data model should inherit from a standard data model that is already defined inside the OpenBB Platform. All standard models are created and maintained by the OpenBB team.
Usage of these models will unlock a set of perks that are only available to standardized data, namely:
- Can query and output data in a standardized way.
- Can expect extensions that follow standardization to work out-of-the-box.
- Can expect transparently defined schemas for the data that is returned by the API.
- Can expect consistent data types and validation.
- Will work seamlessly with other providers that use the same standard model.
The standard models are defined under the openbb_platform/core/openbb_core/provider/standard_models directory.
They define the QueryParams and Data models, which are used to query and output data. They are pydantic and you can leverage all the pydantic features such as validators.
Standardization Caveats
The standardization framework is a very powerful tool, but it has some caveats that you should be aware of:
- We standardize fields that are shared between two or more providers. If there is a third provider that doesn't share the same fields, we will declare it as an optional field (
| Nonewith aNonedefault). - When mapping the column names from a provider-specific model to the standard model, the CamelCase to snake_case conversion is done automatically. If the column names are not the same, you'll need to manually map them. (e.g.
o->open) - The standard models are created and maintained by the OpenBB team. If you want to add a new field to a standard model, you'll need to open a PR to the OpenBB Platform.
Standard QueryParams Example
class EquityHistoricalQueryParams(QueryParams):
"""Equity Historical Price Query."""
symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol", ""))
start_date: dateType | None = Field(
default=None,
description=QUERY_DESCRIPTIONS.get("start_date", ""),
)
end_date: dateType | None = Field(
default=None,
description=QUERY_DESCRIPTIONS.get("end_date", ""),
)
The QueryParams is an abstract class that just tells us that we are dealing with query parameters.
The OpenBB Platform dynamically knows where the standard models begin in the inheritance tree, so you don't need to worry about it.
Standard Data Example
class EquityHistoricalData(Data):
"""Equity Historical Price Data."""
date: dateType | datetime = Field(description=DATA_DESCRIPTIONS.get("date", ""))
open: float = Field(description=DATA_DESCRIPTIONS.get("open", ""))
high: float = Field(description=DATA_DESCRIPTIONS.get("high", ""))
low: float = Field(description=DATA_DESCRIPTIONS.get("low", ""))
close: float = Field(description=DATA_DESCRIPTIONS.get("close", ""))
volume: float | int | None = Field(
default=None, description=DATA_DESCRIPTIONS.get("volume", "")
)
vwap: float | None = Field(
default=None, description=DATA_DESCRIPTIONS.get("vwap", "")
)
The Data class is an abstract class that tells us the expected output data. Here we can see a vwap field that is optional. This is because not all providers share this field while it is shared between two or more providers.
What is an extension?
An extension adds functionality to the OpenBB Platform. It can be a new data source, a new command, a new visualization, etc.
Types of extensions
We primarily have 3 types of extensions:
- OpenBB Extensions - built and maintained by the OpenBB team (e.g.
openbb-equity) - Community Extensions - built by anyone and primarily maintained by OpenBB (e.g.
openbb-tmx) - Independent Extensions - built and maintained independently by anyone
If your extension is of high quality and you think that it would be a good community extension, you can open a PR to the OpenBB Platform repository and we'll review it.
We encourage independent extensions to be shared with the community by publishing them to PyPI.
Dependency Management
High-Level Overview
- Core (
openbb-core): The runtime. It ships no data and no commands; it discovers installed extensions through entry points and assembles them into the Python interface and the REST API. - Routers (
openbb_core_extensionentry point): Command namespaces such asobb.equityorobb.economy. Each router is its own package underopenbb_platform/extensions. - Providers (
openbb_provider_extensionentry point): Data sources that implement router commands. Each provider is its own package underopenbb_platform/providers, and may also register its own router. - OBBject extensions (
openbb_obbject_extensionentry point): Accessors added to every command result, such asopenbb-charting. They live underopenbb_platform/obbject_extensions. - Tooling:
openbb-platform-api,openbb-mcp-server, andopenbb-devtools, underopenbb_platform/extensions.
Package Layout
Every package is a standalone project with its own pyproject.toml and uv.lock:
[build-system]useshatchling.[project]holds the metadata,dependencies,optional-dependencies, and entry points.[dependency-groups]holds thedevgroup used for tests and linting.[tool.uv.sources]points sibling OpenBB packages (openbb-core,openbb-devtools, ...) at their local paths as editable installs.
Installation
For development setup, use the provided script to install every tracked, published package in editable mode, with all optional extras and dev dependency groups, then build the static openbb package. It requires uv.
- From the root of the repo call
python openbb_platform/dev_install.py --routers - Leave out
--routersto skip the router extensions (openbb-equity,openbb-economy,openbb-fixedincome, and the rest);openbb-newsis always installed.
Note
: If developing a single extension, you can install just that package with
uv pip install -e . --group devfrom its directory instead.
Managing Dependencies with uv
Run these from the package directory, next to its pyproject.toml. Each one updates both pyproject.toml and uv.lock.
- Add a Dependency:
uv add <my-dependency> - Add a Development Dependency:
uv add --group dev <my-dependency> - Remove a Dependency:
uv remove <my-dependency> - Update the Lock File:
uv lock - Upgrade a Dependency:
uv lock --upgrade-package <my-dependency>
Developer Guidelines
Expectations for Developers
-
Use Cases:
- Ensure that your extensions or features align with the broader goals of the application.
- Understand that the OpenBB Platform is designed to be foundational; build in a way that complements and doesn't conflict with its core functionalities.
-
Documentation:
- Provide clear and comprehensive documentation for any new feature or extension you develop.
-
Code Quality:
- Adhere to the coding standards and conventions of the OpenBB Platform.
- Ensure your code is maintainable and well-organized.
-
Testing:
- Thoroughly test any new feature or extension to ensure it works as expected.
-
Performance:
- Ensure that your extensions or features do not adversely affect the performance of the OpenBB Platform.
- Optimize for scalability, especially if you anticipate high demand for your feature.
-
Collaboration:
- Engage with the OpenBB community to gather feedback on your developments.
How to build OpenBB extensions?
We have a Cookiecutter template that will help you get started. It serves as a jumpstart for your extension development, so you can focus on the data and not on the boilerplate.
uvx openbb-cookiecutter
Please refer to the Cookiecutter template and follow the instructions there.
This document will walk you through the steps of adding a new extension to the OpenBB Platform.
The high level steps are:
- Generate the extension structure
- Install your dependencies
- Install your new package
- Use your extension (either from Python or the API interface)
- QA your extension
- Share your extension with the community
Building Extensions: Best Practices
- Review Platform Dependencies: Before adding any dependency, ensure it aligns with the Platform's existing dependencies.
- Use Loose Versioning: If possible, specify a range to maintain compatibility. E.g.,
>=1.4,<1.5. - Testing: Test your extension with the Platform's core to avoid conflicts. Both unit and integration tests are recommended.
- Document Dependencies: Keep
pyproject.tomlanduv.lockup to date, so the dependency record is clear.
How to add a new data point?
In this section, we'll be adding a new data point to the OpenBB Platform. We will add a new provider with an existing standard data model.
Identify which type of data you want to add
In this example, we'll be adding OHLC stock data that is used by the obb.equity.price.historical command.
Note that, if no command exists for your data, we need to add one under the right router. Each router is categorized under different extensions (equity, currency, crypto, etc.).
Check if the standard model exists
Given the fact that there's already an endpoint for OHLCV stock data, we can check if the standard exists.
In this case, it's EquityHistorical which can be found in openbb_platform/core/openbb_core/provider/standard_models/equity_historical.py.
If the standard model doesn't exist:
- you won't need to inherit from it in the next steps.
- all your provider query parameters will be under the
**kwargsin the python interface. - it might not work out-of-the box with other extensions that follow standardization e.g. the
chartingextension
Create Query Parameters model
Query Parameters are the parameters that are passed to the API endpoint in order to make the request.
For the EquityHistorical example, this would look like the following:
class <ProviderName>EquityHistoricalQueryParams(EquityHistoricalQueryParams):
"""<ProviderName> Equity Historical Query.
Source: https://www.<provider_name>.co/documentation/
"""
Add any provider-specific query parameters as extra fields.
Create Data Output model
The data output is the data that is returned by the API endpoint.
For the EquityHistorical example, this would look like the following:
class <ProviderName>EquityHistoricalData(EquityHistoricalData):
"""<ProviderName> Equity Historical Data.
Source: https://www.<provider_name>.co/documentation/
"""
Add any provider-specific output fields as extra fields.
Note that, since
EquityHistoricalDatainherits from pydantic'sBaseModel, we can leverage validators to perform additional checks on the output model. A very good example of this, would be to transform a string date into a datetime object.
Build the Fetcher
The Fetcher class is responsible for making the request to the API endpoint and providing the output.
It will receive the query parameters, and it will return the output while leveraging the pydantic model schemas.
For the EquityHistorical example, this would look like the following:
class <ProviderName>EquityHistoricalFetcher(
Fetcher[
<ProviderName>EquityHistoricalQueryParams,
list[<ProviderName>EquityHistoricalData],
]
):
"""Transform the query, extract and transform the data."""
@staticmethod
def transform_query(params: dict[str, Any]) -> <ProviderName>EquityHistoricalQueryParams:
"""Transform the query parameters."""
return <ProviderName>EquityHistoricalQueryParams(**params)
@staticmethod
async def aextract_data(
query: <ProviderName>EquityHistoricalQueryParams,
credentials: dict[str, str] | None,
**kwargs: Any,
) -> list[dict]:
"""Return the raw data from the endpoint."""
return await my_request(query, credentials, **kwargs)
@staticmethod
def transform_data(
query: <ProviderName>EquityHistoricalQueryParams,
data: list[dict],
**kwargs: Any,
) -> list[<ProviderName>EquityHistoricalData]:
"""Transform the data to the standard format."""
return [<ProviderName>EquityHistoricalData.model_validate(d) for d in data]
A Fetcher implements either the asynchronous aextract_data or the synchronous extract_data.
Make sure that you're following the TET pattern when building a
Fetcher- Transform, Extract, Transform. See more on this here.
By default the credentials declared on each Provider are required. This means that before a query is executed, we check that all the credentials are present and if not an exception is raised. If you want to make credentials optional on a given fetcher, even though they are declared on the Provider, you can add require_credentials=False to the Fetcher
class. See the following example:
class <ProviderName>EquityHistoricalFetcher(
Fetcher[
<ProviderName>EquityHistoricalQueryParams,
list[<ProviderName>EquityHistoricalData],
]
):
"""Transform the query, extract and transform the data."""
require_credentials = False
Make the provider visible
In order to make the new provider visible to the OpenBB Platform, define a Provider in the __init__.py file of the providers/<provider_name>/openbb_<provider_name>/ folder.
"""<Provider Name> Provider module."""
from openbb_core.provider.abstract.provider import Provider
from openbb_<provider_name>.models.equity_historical import <ProviderName>EquityHistoricalFetcher
<provider_name>_provider = Provider(
name="<provider_name>",
website="<URL to the provider website>",
description="Provider description goes here",
credentials=["api_key"],
fetcher_dict={
"EquityHistorical": <ProviderName>EquityHistoricalFetcher,
},
)
If the provider does not require any credentials, you can remove that parameter. On the other hand, if it requires more than one item to authenticate, add each of them to the credentials list. Users supply each one as <provider_name>_<credential>, e.g. <provider_name>_api_key.
Then register it as an entry point in the provider's pyproject.toml:
[project.entry-points."openbb_provider_extension"]
<provider_name> = "openbb_<provider_name>:<provider_name>_provider"
After running uv pip install -e . in openbb_platform/providers/<provider_name>, your provider is ready for use from both the Python interface and the API.
How to add custom data sources?
You will get your data either from a CSV file, local database or from an API endpoint.
If you don't want or don't need to partake in the data standardization framework, you have the option to add all the logic straight inside the router file. This is usually the case when you are returning custom data from your local CSV file, or similar. Keep in mind that we also serve the REST API and that you shouldn't send non-serializable objects as a response (e.g. a pandas dataframe).
Saying that, we highly recommend following the standardization framework, as it will make your life easier in the long run and unlock a set of features that are only available to standardized data.
When standardizing, all data is defined using two different pydantic models:
- Define the query parameters model.
- Define the resulting data schema model.
The models can be entirely custom, or inherit from the OpenBB standardized models. They enforce a safe and consistent data structure, validation and type checking.
We call this the Know-Your-Data principle.
After you've defined both models, you'll need to define a Fetcher class which contains three methods:
transform_query- transforms the query parameters to the format of the API endpoint.aextract_dataorextract_data- makes the request to the API endpoint and returns the raw data.transform_data- transforms the raw data into the defined data model.
Note that the
Fetchershould inherit from theFetcherclass, which is a generic class that receives the query parameters and the data model as type parameters.
After finalizing your models, you need to make them visible to the OpenBB Platform. This is done by adding the Fetcher to the fetcher_dict of the Provider defined in your package's __init__.py, and registering that Provider as an openbb_provider_extension entry point.
Any command that uses the Fetcher class you've just defined will call the transform_query, aextract_data (or extract_data) and transform_data methods under the hood in order to get the data and output it to the end user.
If you're not sure what's a command and why is it even using the Fetcher class, follow along!
OpenBB Platform commands
The OpenBB Platform will enable you to query and output your data in a very simple way.
Any Platform endpoint will be available both from a Python interface and the API.
The command definition on the Platform follows FastAPI conventions, meaning that you'll be creating endpoints.
The Cookiecutter template generates for you a router file with a set of examples that you can follow, namely:
- Perform a simple
GETandPOSTrequest - without worrying on any custom data definition. - Using a custom data definition so you get your data the exact way you want it.
You can expect the following endpoint structure when using a Fetcher to serve the data:
@router.command(
model="Example",
examples=[APIEx(parameters={"symbol": "AAPL", "provider": "<provider_name>"})],
)
async def model_example(
cc: CommandContext,
provider_choices: ProviderChoices,
standard_params: StandardParams,
extra_params: ExtraParams,
) -> OBBject:
"""Example Data."""
return await OBBject.from_query(Query(**locals()))
Let's break it down:
@router.command(...)- this tells the OpenBB Platform that this is a command.model="Example"- this is the name of theFetcherdictionary key that you've defined in thefetcher_dictof yourProvider.examples=[...]- example parameters, used in the generated docstrings and to build the integration tests.cc: CommandContext- this contains a set of user and system settings that is useful during the execution of the command - eg. api keys.provider_choices: ProviderChoices- all the providers that implement theExampleFetcher.standard_params: StandardParams- standardized parameters that are common to all providers that implement theExampleFetcher.extra_params: ExtraParams- it contains the provider specific arguments that are not standardized.
You only need to change the model parameter to the name of the Fetcher dictionary key and everything else will be handled by the OpenBB Platform.
The router itself is registered as an entry point in the extension's pyproject.toml:
[project.entry-points."openbb_core_extension"]
<router_name> = "openbb_<router_name>.<router_name>_router:router"
Architectural considerations
Important classes
from openbb_core.app.model.obbject import OBBject
from openbb_core.app.router import Router
from openbb_core.provider.abstract.data import Data
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.abstract.provider import Provider
from openbb_core.provider.abstract.query_params import QueryParams
The TET pattern
The TET pattern is a pattern that we use to build the Fetcher classes. It stands for Transform, Extract, Transform.
As the OpenBB Platform has its own standardization framework and the data fetcher are a very important part of it, we need to ensure that the data is transformed and extracted in a consistent way, to help us do that, we came up with the TET pattern, which helps us build and ship faster as we have a clear structure on how to build the Fetcher classes.
- Transform -
transform_query(params: dict[str, Any]): transforms the query parameters. Given aparamsdictionary this method should return the transformed query parameters as aQueryParamschild so that we can leverage the pydantic model schemas and validation into the next step. This might also be the place do perform some transformations on any given parameter, i.e., if you want to transform an empty date into adatetime.now().date(). - Extract -
aextract_data(query: ExampleQueryParams, credentials: dict[str, str] | None, **kwargs: Any)(or the synchronousextract_data): makes the request to the API endpoint and returns the raw data. Given the transformed query parameters, the credentials and any other extra arguments, this method should return the raw data. - Transform -
transform_data(query: ExampleQueryParams, data: Any, **kwargs: Any) -> list[ExampleData]: transforms the raw data into the defined data model. Given the transformed query parameters (might be useful for some filtering), the raw data and any other extra arguments, this method should return the transformed data as a list ofDatachildren.
Errors
To ensure a consistent error handling behavior our API relies on the convention below, implemented in exception_handlers.py.
| Status code | Exception | Detail | Description |
|---|---|---|---|
| 204 | EmptyDataError |
No content. | Raise when the query succeeded but returned no data. |
| 400 | OpenBBError or a child of OpenBBError |
Custom message. | Use this to explicitly raise custom exceptions. |
| 422 | ValidationError, ValueError |
Pydantic errors dict message. |
Raised to inform the user about query validation errors. ValidationErrors outside of the query are treated with status code 500. |
| 502 | UnauthorizedError |
Custom message. | Raise when the upstream provider rejects the credentials. |
| 500 | Any exception not covered above, eg ZeroDivisionError |
Unexpected error. | Unexpected exceptions, most likely a bug. |
OpenBBError lives in openbb_core.app.model.abstract.error; EmptyDataError and UnauthorizedError live in openbb_core.provider.utils.errors.
Data processing commands
The data processing commands are commands that are used to process the data that may or may not come from the OpenBB Platform.
In order to create a data processing framework general enough to be used by any extension, we've created a special abstract class called Data which all standardized (and consequently its child classes) will inherit from.
Why is this important?
So that we can ensure that all OBBject.results will share a common ground on which we can apply out-of-the-box data processing commands, such as the technical, quantitative or econometrics routers.
But what's really the Data class?
It's a pydantic model that inherits from the BaseModel and can contain any given number of extra fields. In practice, it looks as follows:
>>> res = obb.equity.price.historical("AAPL", provider="cboe")
>>> type(res.results[0]).__name__
'CboeEquityHistoricalData'
The
CboeEquityHistoricalDataclass is a child class of theDataclass.
Note how we've indexed to get only the first element of the results list (which represents a single row, if we want to think about it as a tabular output). This simply means that we are getting a list of CboeEquityHistoricalData from the obb.equity.price.historical command. Or, we can also say that that's equivalent to list[Data]!
This is very powerful, as we can now apply any data processing command to the results list, without worrying about the underlying data structure.
That's why, on data processing commands (such as the technical router) we will find the following in the command's parameters:
class EmaQueryParams(QueryParams):
"""Exponential Moving Average query."""
data: list[Data] = Field(description="Price series.")
target: str = "close"
index: str = "date"
length: PositiveInt = 50
offset: int = 0
Does that mean that I can only use the data processing commands if I instantiate a class that inherits from Data?
Not at all! Consider the following example:
>>> from openbb_core.provider.abstract.data import Data
>>> my_data_item_1 = {"open": 1, "high": 2, "low": 3, "close": 4, "volume": 5, "date": "2020-01-01"}
>>> Data.model_validate(my_data_item_1)
Data(open=1, high=2, low=3, close=4, volume=5, date=2020-01-01)
This means that the Data class is clever enough to understand that you are passing a dictionary and it will try to validate it for you.
In other words, if you're using data that doesn't come from the OpenBB Platform, you only need to ensure it's parsable by the Data class and you'll be able to use the data processing commands.
For example, imagine you have a dataframe that you want to use with the technical router. You can do the following:
>>> res = obb.equity.price.historical("AAPL", provider="cboe")
>>> df = res.to_dataframe()
>>> obb.technical.ema(data=df, length=50)
Note that for this example we've used the
OBBject.to_dataframe()method to have an example dataframe, but it could be any other dataframe that you have.
Python Interface
When using the OpenBB Platform on a Python Interface, docstrings and type hints are your best friends as they provide plenty of context on how to use the commands.
Looking at the generated obb.technical.ema signature:
def ema(
self,
data: list | dict | DataFrame | list[DataFrame] | Series | list[Series] | ndarray | Data | list[Data],
target: str = "close",
index: str = "date",
length: int = 50,
offset: int = 0,
chart: bool = False,
) -> OBBject:
...
We can easily deduce that the ema command accepts records, Data, pandas objects, or numpy arrays. The chart parameter appears when openbb-charting is installed.
API Interface
When using the OpenBB Platform on a API Interface, the types are a bit more limited than on the Python one, as, for example, we can't use pandas.DataFrame as a type. However the same principles apply for what Data means, i.e., any given data processing command, which are characterized as POST endpoints on the API, will accept data as a list of records on
the request body, i.e.:
[
{
"date": "2023-11-03",
"open": 80,
"high": 80.69,
"low": 77.37,
"close": 77.62,
"volume": 2487300
}
]
Contributor Guidelines
The Contributor Guidelines are intended to be a continuation of the Developer Guidelines. They are not a replacement, but rather an expansion, focusing specifically on those who seek to directly enhance the OpenBB Platform's codebase. It's crucial for Contributors to be familiar with both sets of guidelines to ensure a harmonious and productive engagement with the OpenBB Platform.
There are many ways to contribute to the OpenBB Platform. You can add a new data point, add a new command, add a new visualization, add a new extension, fix a bug, improve or create documentation, etc.
Expectations for Contributors
-
Use Cases:
- Ensure that your contributions directly enhance the OpenBB Platform's functionality or extension ecosystem.
-
Documentation:
- All code contributions should come with relevant documentation, including the purpose of the contribution, how it works, and any changes it makes to existing functionalities.
- Update any existing documentation if your contribution alters the behavior of the OpenBB Platform.
-
Code Quality:
- Your code should adhere strictly to the OpenBB Platform's coding standards and conventions.
- Ensure clarity, maintainability, and proper organization in your code.
-
Testing:
- All contributions must be thoroughly tested to avoid introducing bugs to the OpenBB Platform.
- Contributions should include relevant automated tests (unit and integration), and any new feature should come with its test cases.
-
Performance:
- Your contributions should be optimized for performance and should not degrade the overall efficiency of the OpenBB Platform.
- Address any potential bottlenecks and ensure scalability.
-
Collaboration:
- Engage actively with the OpenBB development team to ensure that your contributions align with the platform's roadmap and standards.
- Welcome feedback and be open to making revisions based on reviews and suggestions from the community.
Quality Assurance
We are strong believers in the Quality Assurance (QA) process and we want to make sure that all the extensions that are added to the OpenBB Platform are of high quality. To ensure this, we have a set of QA tools that you can use to test your extension.
Primarily, we have tools that semi-automate the creation of unit and integration tests.
The QA tools are still in development and we are constantly improving them.
Linting and type checking
Each package is linted and type checked from its own directory, the same way its CI workflow runs:
cd openbb_platform/providers/<provider_name>
ruff format --check .
ruff check .
ty check openbb_<provider_name>
The repository's pre-commit hooks run the same checks on staged files:
pre-commit install
Unit tests
Each Fetcher comes equipped with a test method that will ensure that it is implemented correctly and that it is returning the expected data. It also ensures that all types are correct and that the data is valid.
To create unit tests for your Fetchers, you can run the following command:
python openbb_platform/providers/tests/utils/unit_tests_generator.py
Note that you should be running this file from the root of the repository. Note that the
testsfolder must exist in order to generate the tests.
The automatic unit test generation will add unit tests for all the fetchers available in a given provider.
To record the HTTP cassettes for the unit tests, you can run the following command:
pytest <path_to_the_unit_test_file> --record=all
Note that sometimes manual intervention is needed. For example, adjusting out-of-top level imports or adding specific arguments for a given fetcher.
Run a package's unit tests from its directory:
cd openbb_platform/providers/<provider_name>
pytest tests
Integration tests
The integration tests are a bit more complex than the unit tests, as we want to test both the Python interface and the API interface. They live in the integration folder of each router extension, and generating them requires the routers to be installed (python openbb_platform/dev_install.py --routers). We have two scripts that will help you generate them.
To generate the integration tests for the Python interface, you can run the following command:
python openbb_platform/extensions/tests/utils/integration_tests_generator.py
To generate the integration tests for the API interface, you can run the following command:
python openbb_platform/extensions/tests/utils/integration_tests_api_generator.py
When testing the API interface, you'll need to run the OpenBB Platform locally on port 8000 before running the tests. To do so, you can run the following command:
uvicorn openbb_core.api.rest_api:app --host 0.0.0.0 --port 8000 --reload
These automated tests are a great way to reduce the amount of code you need to write, but they are not a replacement for manual testing and might require tweaking. That's why we have unit tests that test the generated integration tests to ensure they cover all providers and parameters.
Run a router's integration tests from its directory:
cd openbb_platform/extensions/<router_name>
pytest integration -m integration
Import time
We aim to have a short import time for the package. To measure that we use tuna.
To visualize the import time breakdown by module and find potential bottlenecks, run the following commands:
pip install tuna
python -X importtime -c "import openbb" 2> import.log
tuna import.log
Sharing your extension
We encourage you to share your extension with the community. You can do that by publishing it to PyPI.
Publish your extension to PyPI
To publish your extension to PyPI, you'll need to have a PyPI account and a PyPI API token from https://pypi.org/manage/account/token/.
Build
cd into the directory where your extension pyproject.toml lives, make sure that pyproject.toml specifies the version you want to release, and run:
uv build --no-sources
--no-sources builds against the published versions of your OpenBB dependencies instead of the local [tool.uv.sources] paths.
This will create a dist folder in the directory, which will contain the .whl and .tar.gz files matching the version to release.
If you want to test your package locally you can do it with:
pip install dist/openbb_[FILE_NAME].whl
Publish
To publish your package to PyPI run:
uv publish --token pypi-YYYYYYYY
Now, you can pip install your package from PyPI with:
pip install openbb-some_ext
Manage extensions
To install an extension hosted on PyPI, use the pip install <extension> command.
To install an extension that is developed locally, ensure that it contains a pyproject.toml file and then use the pip install <extension> command.
To install the extension in editable mode using pip, add the
-eargument.
Alternatively, dev_install.py installs every package whose pyproject.toml is tracked by git under openbb_platform/core, openbb_platform/extensions/*, openbb_platform/obbject_extensions/*, openbb_platform/providers/*, or cli. Once the extension's pyproject.toml is committed there, python openbb_platform/dev_install.py installs it (add --routers for a router extension).
After installing or removing an extension, run openbb-build to regenerate the openbb package.
Add an extension as a dependency
To add the openbb-technical extension as a dependency, add it to the dependencies of the [project] table in your pyproject.toml:
[project]
dependencies = [
"openbb-technical>=2.0.0",
]
Or run uv add openbb-technical from the package directory. Then you can follow the same process as above to install the extension.
Write code and commit
How to create a PR?
To create a PR to the OpenBB Platform, you'll need to fork the repository and create a new branch.
- Create your Feature Branch, e.g.
git checkout -b feature/AmazingFeature - Check the files you have touched using
git status - Stage the files you want to commit, e.g.
git add openbb_platform/core/openbb_core/app/constants.py. Note: DON'T add any files with personal information. - Write a concise commit message under 50 characters, e.g.
git commit -m "meaningful commit message". If your PR solves an issue raised by a user, you may specify such an issue by adding #ISSUE_NUMBER to the commit message, so that these get linked. Note: If you installed pre-commit hooks and one of the formatters re-formats your code, you'll need to go back to step 3 to add these.
Branch Naming Conventions
The accepted branch naming conventions are:
feature/feature-namehotfix/hotfix-name
These branches can only have PRs pointing to the develop branch.