Upload 225 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- openbb_platform/extensions/README.md +3 -0
- openbb_platform/extensions/__init__.py +1 -0
- openbb_platform/extensions/commodity/README.md +13 -0
- openbb_platform/extensions/commodity/integration/test_commodity_api.py +112 -0
- openbb_platform/extensions/commodity/integration/test_commodity_python.py +100 -0
- openbb_platform/extensions/commodity/openbb_commodity/__init__.py +1 -0
- openbb_platform/extensions/commodity/openbb_commodity/commodity_router.py +80 -0
- openbb_platform/extensions/commodity/openbb_commodity/price/__init__.py +1 -0
- openbb_platform/extensions/commodity/openbb_commodity/price/price_router.py +33 -0
- openbb_platform/extensions/commodity/poetry.lock +0 -0
- openbb_platform/extensions/commodity/pyproject.toml +19 -0
- openbb_platform/extensions/crypto/README.md +13 -0
- openbb_platform/extensions/crypto/integration/test_crypto_api.py +126 -0
- openbb_platform/extensions/crypto/integration/test_crypto_python.py +117 -0
- openbb_platform/extensions/crypto/openbb_crypto/__init__.py +1 -0
- openbb_platform/extensions/crypto/openbb_crypto/crypto_router.py +35 -0
- openbb_platform/extensions/crypto/openbb_crypto/crypto_views.py +22 -0
- openbb_platform/extensions/crypto/openbb_crypto/price/__init__.py +1 -0
- openbb_platform/extensions/crypto/openbb_crypto/price/price_router.py +58 -0
- openbb_platform/extensions/crypto/openbb_crypto/py.typed +0 -0
- openbb_platform/extensions/crypto/poetry.lock +0 -0
- openbb_platform/extensions/crypto/pyproject.toml +22 -0
- openbb_platform/extensions/crypto/tests/.gitkeep +0 -0
- openbb_platform/extensions/currency/README.md +13 -0
- openbb_platform/extensions/currency/integration/test_currency_api.py +198 -0
- openbb_platform/extensions/currency/integration/test_currency_python.py +183 -0
- openbb_platform/extensions/currency/openbb_currency/__init__.py +1 -0
- openbb_platform/extensions/currency/openbb_currency/currency_router.py +99 -0
- openbb_platform/extensions/currency/openbb_currency/currency_views.py +22 -0
- openbb_platform/extensions/currency/openbb_currency/price/__init__.py +1 -0
- openbb_platform/extensions/currency/openbb_currency/price/price_router.py +53 -0
- openbb_platform/extensions/currency/openbb_currency/py.typed +0 -0
- openbb_platform/extensions/currency/poetry.lock +0 -0
- openbb_platform/extensions/currency/pyproject.toml +22 -0
- openbb_platform/extensions/currency/tests/.gitkeep +0 -0
- openbb_platform/extensions/derivatives/README.md +13 -0
- openbb_platform/extensions/derivatives/integration/test_derivatives_api.py +224 -0
- openbb_platform/extensions/derivatives/integration/test_derivatives_python.py +198 -0
- openbb_platform/extensions/derivatives/openbb_derivatives/__init__.py +1 -0
- openbb_platform/extensions/derivatives/openbb_derivatives/derivatives_router.py +10 -0
- openbb_platform/extensions/derivatives/openbb_derivatives/derivatives_views.py +244 -0
- openbb_platform/extensions/derivatives/openbb_derivatives/futures/__init__.py +1 -0
- openbb_platform/extensions/derivatives/openbb_derivatives/futures/futures_router.py +97 -0
- openbb_platform/extensions/derivatives/openbb_derivatives/options/__init__.py +1 -0
- openbb_platform/extensions/derivatives/openbb_derivatives/options/options_router.py +74 -0
- openbb_platform/extensions/derivatives/poetry.lock +0 -0
- openbb_platform/extensions/derivatives/pyproject.toml +22 -0
- openbb_platform/extensions/derivatives/tests/.gitkeep +0 -0
- openbb_platform/extensions/devtools/README.md +22 -0
- openbb_platform/extensions/devtools/integration/.gitkeep +0 -0
openbb_platform/extensions/README.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Extensions
|
| 2 |
+
|
| 3 |
+
In this folder you can find the extensions that were created or are supported by OpenBB.
|
openbb_platform/extensions/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""OpenBB Platform Extensions."""
|
openbb_platform/extensions/commodity/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Commodity Extension for OpenBB Platform
|
| 2 |
+
|
| 3 |
+
This extension provides a set of commands for commodity-related data.
|
| 4 |
+
|
| 5 |
+
## Installation
|
| 6 |
+
|
| 7 |
+
To install the extension, run the following command in this folder:
|
| 8 |
+
|
| 9 |
+
```bash
|
| 10 |
+
pip install openbb-commodity
|
| 11 |
+
```
|
| 12 |
+
|
| 13 |
+
Documentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).
|
openbb_platform/extensions/commodity/integration/test_commodity_api.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Test Commodity API endpoints."""
|
| 2 |
+
|
| 3 |
+
import base64
|
| 4 |
+
|
| 5 |
+
import pytest
|
| 6 |
+
import requests
|
| 7 |
+
from openbb_core.env import Env
|
| 8 |
+
from openbb_core.provider.utils.helpers import get_querystring
|
| 9 |
+
|
| 10 |
+
# pylint: disable=redefined-outer-name
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
@pytest.fixture(scope="session")
|
| 14 |
+
def headers():
|
| 15 |
+
"""Get the headers for the API request."""
|
| 16 |
+
userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}"
|
| 17 |
+
userpass_bytes = userpass.encode("ascii")
|
| 18 |
+
base64_bytes = base64.b64encode(userpass_bytes)
|
| 19 |
+
|
| 20 |
+
return {"Authorization": f"Basic {base64_bytes.decode('ascii')}"}
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
@pytest.mark.parametrize(
|
| 24 |
+
"params",
|
| 25 |
+
[
|
| 26 |
+
(
|
| 27 |
+
{
|
| 28 |
+
"commodity": "all",
|
| 29 |
+
"start_date": None,
|
| 30 |
+
"end_date": None,
|
| 31 |
+
"frequency": None,
|
| 32 |
+
"transform": None,
|
| 33 |
+
"aggregation_method": None,
|
| 34 |
+
"provider": "fred",
|
| 35 |
+
}
|
| 36 |
+
),
|
| 37 |
+
],
|
| 38 |
+
)
|
| 39 |
+
@pytest.mark.integration
|
| 40 |
+
def test_commodity_price_spot(params, headers):
|
| 41 |
+
"""Test the commodity spot prices endpoint."""
|
| 42 |
+
params = {p: v for p, v in params.items() if v}
|
| 43 |
+
|
| 44 |
+
query_str = get_querystring(params, [])
|
| 45 |
+
url = f"http://0.0.0.0:8000/api/v1/commodity/price/spot?{query_str}"
|
| 46 |
+
result = requests.get(url, headers=headers, timeout=10)
|
| 47 |
+
assert isinstance(result, requests.Response)
|
| 48 |
+
assert result.status_code == 200
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
@pytest.mark.parametrize(
|
| 52 |
+
"params",
|
| 53 |
+
[
|
| 54 |
+
(
|
| 55 |
+
{
|
| 56 |
+
"category": "balance_sheet",
|
| 57 |
+
"table": "stocks",
|
| 58 |
+
"start_date": None,
|
| 59 |
+
"end_date": None,
|
| 60 |
+
"provider": "eia",
|
| 61 |
+
"use_cache": True,
|
| 62 |
+
}
|
| 63 |
+
),
|
| 64 |
+
(
|
| 65 |
+
{
|
| 66 |
+
"category": "weekly_estimates",
|
| 67 |
+
"table": "crude_production",
|
| 68 |
+
"start_date": "2020-01-01",
|
| 69 |
+
"end_date": "2023-12-31",
|
| 70 |
+
"provider": "eia",
|
| 71 |
+
"use_cache": True,
|
| 72 |
+
}
|
| 73 |
+
),
|
| 74 |
+
],
|
| 75 |
+
)
|
| 76 |
+
@pytest.mark.integration
|
| 77 |
+
def test_commodity_petroleum_status_report(params, headers):
|
| 78 |
+
"""Test the Petroleum Status Report endpoint."""
|
| 79 |
+
params = {p: v for p, v in params.items() if v}
|
| 80 |
+
|
| 81 |
+
query_str = get_querystring(params, [])
|
| 82 |
+
url = f"http://0.0.0.0:8000/api/v1/commodity/petroleum_status_report?{query_str}"
|
| 83 |
+
result = requests.get(url, headers=headers, timeout=10)
|
| 84 |
+
assert isinstance(result, requests.Response)
|
| 85 |
+
assert result.status_code == 200
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
@pytest.mark.parametrize(
|
| 89 |
+
"params",
|
| 90 |
+
[
|
| 91 |
+
(
|
| 92 |
+
{
|
| 93 |
+
"table": "01",
|
| 94 |
+
"symbol": None,
|
| 95 |
+
"start_date": "2024-09-01",
|
| 96 |
+
"end_date": "2024-10-01",
|
| 97 |
+
"provider": "eia",
|
| 98 |
+
"frequency": "month",
|
| 99 |
+
}
|
| 100 |
+
),
|
| 101 |
+
],
|
| 102 |
+
)
|
| 103 |
+
@pytest.mark.integration
|
| 104 |
+
def test_commodity_short_term_energy_outlook(params, headers):
|
| 105 |
+
"""Test the Short Term Energy Outlook endpoint."""
|
| 106 |
+
params = {p: v for p, v in params.items() if v}
|
| 107 |
+
|
| 108 |
+
query_str = get_querystring(params, [])
|
| 109 |
+
url = f"http://0.0.0.0:8000/api/v1/commodity/short_term_energy_outlook?{query_str}"
|
| 110 |
+
result = requests.get(url, headers=headers, timeout=10)
|
| 111 |
+
assert isinstance(result, requests.Response)
|
| 112 |
+
assert result.status_code == 200
|
openbb_platform/extensions/commodity/integration/test_commodity_python.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Test Commodity extension."""
|
| 2 |
+
|
| 3 |
+
import pytest
|
| 4 |
+
from openbb_core.app.model.obbject import OBBject
|
| 5 |
+
|
| 6 |
+
# pylint: disable=redefined-outer-name
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
@pytest.fixture(scope="session")
|
| 10 |
+
def obb(pytestconfig): # pylint: disable=inconsistent-return-statements
|
| 11 |
+
"""Fixture to setup obb."""
|
| 12 |
+
if pytestconfig.getoption("markexpr") != "not integration":
|
| 13 |
+
import openbb # pylint: disable=import-outside-toplevel
|
| 14 |
+
|
| 15 |
+
return openbb.obb
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@pytest.mark.parametrize(
|
| 19 |
+
"params",
|
| 20 |
+
[
|
| 21 |
+
(
|
| 22 |
+
{
|
| 23 |
+
"commodity": "all",
|
| 24 |
+
"start_date": None,
|
| 25 |
+
"end_date": None,
|
| 26 |
+
"frequency": None,
|
| 27 |
+
"transform": None,
|
| 28 |
+
"aggregation_method": None,
|
| 29 |
+
"provider": "fred",
|
| 30 |
+
}
|
| 31 |
+
),
|
| 32 |
+
],
|
| 33 |
+
)
|
| 34 |
+
@pytest.mark.integration
|
| 35 |
+
def test_commodity_price_spot(params, obb):
|
| 36 |
+
"""Test the commodity spot prices endpoint."""
|
| 37 |
+
params = {p: v for p, v in params.items() if v}
|
| 38 |
+
|
| 39 |
+
result = obb.commodity.price.spot(**params)
|
| 40 |
+
assert result
|
| 41 |
+
assert isinstance(result, OBBject)
|
| 42 |
+
assert len(result.results) > 0
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
@pytest.mark.parametrize(
|
| 46 |
+
"params",
|
| 47 |
+
[
|
| 48 |
+
(
|
| 49 |
+
{
|
| 50 |
+
"category": "balance_sheet",
|
| 51 |
+
"table": "stocks",
|
| 52 |
+
"start_date": None,
|
| 53 |
+
"end_date": None,
|
| 54 |
+
"provider": "eia",
|
| 55 |
+
"use_cache": True,
|
| 56 |
+
}
|
| 57 |
+
),
|
| 58 |
+
(
|
| 59 |
+
{
|
| 60 |
+
"category": "weekly_estimates",
|
| 61 |
+
"table": "crude_production",
|
| 62 |
+
"start_date": "2020-01-01",
|
| 63 |
+
"end_date": "2023-12-31",
|
| 64 |
+
"provider": "eia",
|
| 65 |
+
"use_cache": True,
|
| 66 |
+
}
|
| 67 |
+
),
|
| 68 |
+
],
|
| 69 |
+
)
|
| 70 |
+
@pytest.mark.integration
|
| 71 |
+
def test_commodity_petroleum_status_report(params, obb):
|
| 72 |
+
"""Test Commodity Petroleum Status Report endpoint."""
|
| 73 |
+
result = obb.commodity.petroleum_status_report(**params)
|
| 74 |
+
assert result
|
| 75 |
+
assert isinstance(result, OBBject)
|
| 76 |
+
assert len(result.results) > 0
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
@pytest.mark.parametrize(
|
| 80 |
+
"params",
|
| 81 |
+
[
|
| 82 |
+
(
|
| 83 |
+
{
|
| 84 |
+
"table": "01",
|
| 85 |
+
"symbol": None,
|
| 86 |
+
"start_date": "2024-09-01",
|
| 87 |
+
"end_date": "2024-10-01",
|
| 88 |
+
"provider": "eia",
|
| 89 |
+
"frequency": "month",
|
| 90 |
+
}
|
| 91 |
+
),
|
| 92 |
+
],
|
| 93 |
+
)
|
| 94 |
+
@pytest.mark.integration
|
| 95 |
+
def test_commodity_short_term_energy_outlook(params, obb):
|
| 96 |
+
"""Test Commodity Short Term Energy Outlook endpoint."""
|
| 97 |
+
result = obb.commodity.short_term_energy_outlook(**params)
|
| 98 |
+
assert result
|
| 99 |
+
assert isinstance(result, OBBject)
|
| 100 |
+
assert len(result.results) > 0
|
openbb_platform/extensions/commodity/openbb_commodity/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""OpenBB Commodity Extension."""
|
openbb_platform/extensions/commodity/openbb_commodity/commodity_router.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""The Commodity router."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument,unused-import
|
| 4 |
+
# flake8: noqa: F401
|
| 5 |
+
|
| 6 |
+
# pylint: disable=unused-argument
|
| 7 |
+
|
| 8 |
+
from openbb_core.app.model.command_context import CommandContext
|
| 9 |
+
from openbb_core.app.model.example import APIEx
|
| 10 |
+
from openbb_core.app.model.obbject import OBBject
|
| 11 |
+
from openbb_core.app.provider_interface import (
|
| 12 |
+
ExtraParams,
|
| 13 |
+
ProviderChoices,
|
| 14 |
+
StandardParams,
|
| 15 |
+
)
|
| 16 |
+
from openbb_core.app.query import Query
|
| 17 |
+
from openbb_core.app.router import Router
|
| 18 |
+
|
| 19 |
+
from openbb_commodity.price.price_router import router as price_router
|
| 20 |
+
|
| 21 |
+
router = Router(prefix="", description="Commodity market data.")
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
router.include_router(price_router)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
@router.command(
|
| 28 |
+
model="PetroleumStatusReport",
|
| 29 |
+
examples=[
|
| 30 |
+
APIEx(
|
| 31 |
+
description="Get the EIA's Weekly Petroleum Status Report.",
|
| 32 |
+
parameters={"provider": "eia"},
|
| 33 |
+
),
|
| 34 |
+
APIEx(
|
| 35 |
+
description="Select the category of data, and filter for a specific table within the report.",
|
| 36 |
+
parameters={
|
| 37 |
+
"category": "weekly_estimates",
|
| 38 |
+
"table": "imports",
|
| 39 |
+
"provider": "eia",
|
| 40 |
+
},
|
| 41 |
+
),
|
| 42 |
+
],
|
| 43 |
+
)
|
| 44 |
+
async def petroleum_status_report(
|
| 45 |
+
cc: CommandContext,
|
| 46 |
+
provider_choices: ProviderChoices,
|
| 47 |
+
standard_params: StandardParams,
|
| 48 |
+
extra_params: ExtraParams,
|
| 49 |
+
) -> OBBject:
|
| 50 |
+
"""EIA Weekly Petroleum Status Report."""
|
| 51 |
+
return await OBBject.from_query(Query(**locals()))
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
@router.command(
|
| 55 |
+
model="ShortTermEnergyOutlook",
|
| 56 |
+
examples=[
|
| 57 |
+
APIEx(
|
| 58 |
+
description="Get the EIA's Short Term Energy Outlook.",
|
| 59 |
+
parameters={"provider": "eia"},
|
| 60 |
+
),
|
| 61 |
+
APIEx(
|
| 62 |
+
description="Select the specific table of data from the STEO. Table 03d is World Crude Oil Production.",
|
| 63 |
+
parameters={
|
| 64 |
+
"table": "03d",
|
| 65 |
+
"provider": "eia",
|
| 66 |
+
},
|
| 67 |
+
),
|
| 68 |
+
],
|
| 69 |
+
)
|
| 70 |
+
async def short_term_energy_outlook(
|
| 71 |
+
cc: CommandContext,
|
| 72 |
+
provider_choices: ProviderChoices,
|
| 73 |
+
standard_params: StandardParams,
|
| 74 |
+
extra_params: ExtraParams,
|
| 75 |
+
) -> OBBject:
|
| 76 |
+
"""Monthly short term (18 month) projections using EIA's STEO model.
|
| 77 |
+
|
| 78 |
+
Source: www.eia.gov/steo/
|
| 79 |
+
"""
|
| 80 |
+
return await OBBject.from_query(Query(**locals()))
|
openbb_platform/extensions/commodity/openbb_commodity/price/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""Commodity Price."""
|
openbb_platform/extensions/commodity/openbb_commodity/price/price_router.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Price Router."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from openbb_core.app.model.command_context import CommandContext
|
| 6 |
+
from openbb_core.app.model.example import APIEx
|
| 7 |
+
from openbb_core.app.model.obbject import OBBject
|
| 8 |
+
from openbb_core.app.provider_interface import (
|
| 9 |
+
ExtraParams,
|
| 10 |
+
ProviderChoices,
|
| 11 |
+
StandardParams,
|
| 12 |
+
)
|
| 13 |
+
from openbb_core.app.query import Query
|
| 14 |
+
from openbb_core.app.router import Router
|
| 15 |
+
|
| 16 |
+
router = Router(prefix="/price")
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
@router.command(
|
| 20 |
+
model="CommoditySpotPrices",
|
| 21 |
+
examples=[
|
| 22 |
+
APIEx(parameters={"provider": "fred"}),
|
| 23 |
+
APIEx(parameters={"provider": "fred", "commodity": "wti"}),
|
| 24 |
+
],
|
| 25 |
+
)
|
| 26 |
+
async def spot(
|
| 27 |
+
cc: CommandContext,
|
| 28 |
+
provider_choices: ProviderChoices,
|
| 29 |
+
standard_params: StandardParams,
|
| 30 |
+
extra_params: ExtraParams,
|
| 31 |
+
) -> OBBject:
|
| 32 |
+
"""Commodity Spot Prices."""
|
| 33 |
+
return await OBBject.from_query(Query(**locals()))
|
openbb_platform/extensions/commodity/poetry.lock
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
openbb_platform/extensions/commodity/pyproject.toml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[tool.poetry]
|
| 2 |
+
name = "openbb-commodity"
|
| 3 |
+
version = "1.3.1"
|
| 4 |
+
description = "Commodity extension for OpenBB"
|
| 5 |
+
authors = ["OpenBB Team <hello@openbb.co>"]
|
| 6 |
+
license = "AGPL-3.0-only"
|
| 7 |
+
readme = "README.md"
|
| 8 |
+
packages = [{ include = "openbb_commodity" }]
|
| 9 |
+
|
| 10 |
+
[tool.poetry.dependencies]
|
| 11 |
+
python = ">=3.9.21,<3.13"
|
| 12 |
+
openbb-core = "^1.4.6"
|
| 13 |
+
|
| 14 |
+
[build-system]
|
| 15 |
+
requires = ["poetry-core"]
|
| 16 |
+
build-backend = "poetry.core.masonry.api"
|
| 17 |
+
|
| 18 |
+
[tool.poetry.plugins."openbb_core_extension"]
|
| 19 |
+
commodity = "openbb_commodity.commodity_router:router"
|
openbb_platform/extensions/crypto/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Crypto data extension for OpenBB Platform
|
| 2 |
+
|
| 3 |
+
This extension provides a set of commands for crypto data retrieval.
|
| 4 |
+
|
| 5 |
+
## Installation
|
| 6 |
+
|
| 7 |
+
To install the extension, run the following command in this folder:
|
| 8 |
+
|
| 9 |
+
```bash
|
| 10 |
+
pip install openbb-crypto
|
| 11 |
+
```
|
| 12 |
+
|
| 13 |
+
Documentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).
|
openbb_platform/extensions/crypto/integration/test_crypto_api.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Test crypto API endpoints."""
|
| 2 |
+
|
| 3 |
+
import base64
|
| 4 |
+
|
| 5 |
+
import pytest
|
| 6 |
+
import requests
|
| 7 |
+
from extensions.tests.conftest import parametrize
|
| 8 |
+
from openbb_core.env import Env
|
| 9 |
+
from openbb_core.provider.utils.helpers import get_querystring
|
| 10 |
+
|
| 11 |
+
# pylint: disable=redefined-outer-name
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
@pytest.fixture(scope="session")
|
| 15 |
+
def headers():
|
| 16 |
+
"""Get the headers for the API request."""
|
| 17 |
+
userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}"
|
| 18 |
+
userpass_bytes = userpass.encode("ascii")
|
| 19 |
+
base64_bytes = base64.b64encode(userpass_bytes)
|
| 20 |
+
|
| 21 |
+
return {"Authorization": f"Basic {base64_bytes.decode('ascii')}"}
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
@parametrize(
|
| 25 |
+
"params",
|
| 26 |
+
[
|
| 27 |
+
({"query": "asd"}),
|
| 28 |
+
({"query": "btc", "provider": "fmp"}),
|
| 29 |
+
],
|
| 30 |
+
)
|
| 31 |
+
@pytest.mark.integration
|
| 32 |
+
def test_crypto_search(params, headers):
|
| 33 |
+
"""Test the crypto search endpoint."""
|
| 34 |
+
params = {p: v for p, v in params.items() if v}
|
| 35 |
+
|
| 36 |
+
query_str = get_querystring(params, [])
|
| 37 |
+
url = f"http://0.0.0.0:8000/api/v1/crypto/search?{query_str}"
|
| 38 |
+
result = requests.get(url, headers=headers, timeout=10)
|
| 39 |
+
assert isinstance(result, requests.Response)
|
| 40 |
+
assert result.status_code == 200
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
@parametrize(
|
| 44 |
+
"params",
|
| 45 |
+
[
|
| 46 |
+
(
|
| 47 |
+
{
|
| 48 |
+
"interval": "1d",
|
| 49 |
+
"provider": "fmp",
|
| 50 |
+
"symbol": "BTCUSD",
|
| 51 |
+
"start_date": "2023-01-01",
|
| 52 |
+
"end_date": "2023-01-02",
|
| 53 |
+
}
|
| 54 |
+
),
|
| 55 |
+
(
|
| 56 |
+
{
|
| 57 |
+
"interval": "1h",
|
| 58 |
+
"provider": "fmp",
|
| 59 |
+
"symbol": "BTCUSD,ETHUSD",
|
| 60 |
+
"start_date": None,
|
| 61 |
+
"end_date": None,
|
| 62 |
+
}
|
| 63 |
+
),
|
| 64 |
+
(
|
| 65 |
+
{
|
| 66 |
+
"interval": "1m",
|
| 67 |
+
"sort": "desc",
|
| 68 |
+
"limit": 49999,
|
| 69 |
+
"provider": "polygon",
|
| 70 |
+
"symbol": "BTCUSD",
|
| 71 |
+
"start_date": "2023-01-01",
|
| 72 |
+
"end_date": "2023-01-02",
|
| 73 |
+
}
|
| 74 |
+
),
|
| 75 |
+
(
|
| 76 |
+
{
|
| 77 |
+
"interval": "1d",
|
| 78 |
+
"sort": "desc",
|
| 79 |
+
"limit": 49999,
|
| 80 |
+
"provider": "polygon",
|
| 81 |
+
"symbol": "BTCUSD",
|
| 82 |
+
"start_date": "2023-01-01",
|
| 83 |
+
"end_date": "2023-06-06",
|
| 84 |
+
}
|
| 85 |
+
),
|
| 86 |
+
(
|
| 87 |
+
{
|
| 88 |
+
"interval": "1d",
|
| 89 |
+
"provider": "yfinance",
|
| 90 |
+
"symbol": "BTCUSD",
|
| 91 |
+
"start_date": "2023-01-01",
|
| 92 |
+
"end_date": "2023-01-04",
|
| 93 |
+
}
|
| 94 |
+
),
|
| 95 |
+
(
|
| 96 |
+
{
|
| 97 |
+
"provider": "tiingo",
|
| 98 |
+
"interval": "1d",
|
| 99 |
+
"exchanges": None,
|
| 100 |
+
"symbol": "BTCUSD",
|
| 101 |
+
"start_date": "2023-01-01",
|
| 102 |
+
"end_date": "2023-06-06",
|
| 103 |
+
}
|
| 104 |
+
),
|
| 105 |
+
(
|
| 106 |
+
{
|
| 107 |
+
"provider": "tiingo",
|
| 108 |
+
"interval": "1h",
|
| 109 |
+
"exchanges": ["POLONIEX", "GDAX"],
|
| 110 |
+
"symbol": "BTCUSD",
|
| 111 |
+
"start_date": "2023-01-01",
|
| 112 |
+
"end_date": "2023-01-02",
|
| 113 |
+
}
|
| 114 |
+
),
|
| 115 |
+
],
|
| 116 |
+
)
|
| 117 |
+
@pytest.mark.integration
|
| 118 |
+
def test_crypto_price_historical(params, headers):
|
| 119 |
+
"""Test the crypto historical price endpoint."""
|
| 120 |
+
params = {p: v for p, v in params.items() if v}
|
| 121 |
+
|
| 122 |
+
query_str = get_querystring(params, [])
|
| 123 |
+
url = f"http://0.0.0.0:8000/api/v1/crypto/price/historical?{query_str}"
|
| 124 |
+
result = requests.get(url, headers=headers, timeout=10)
|
| 125 |
+
assert isinstance(result, requests.Response)
|
| 126 |
+
assert result.status_code == 200
|
openbb_platform/extensions/crypto/integration/test_crypto_python.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Test crypto extension."""
|
| 2 |
+
|
| 3 |
+
import pytest
|
| 4 |
+
from extensions.tests.conftest import parametrize
|
| 5 |
+
from openbb_core.app.model.obbject import OBBject
|
| 6 |
+
|
| 7 |
+
# pylint: disable=redefined-outer-name
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
@pytest.fixture(scope="session")
|
| 11 |
+
def obb(pytestconfig): # pylint: disable=inconsistent-return-statements
|
| 12 |
+
"""Fixture to setup obb."""
|
| 13 |
+
if pytestconfig.getoption("markexpr") != "not integration":
|
| 14 |
+
import openbb # pylint: disable=import-outside-toplevel
|
| 15 |
+
|
| 16 |
+
return openbb.obb
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
@parametrize(
|
| 20 |
+
"params",
|
| 21 |
+
[
|
| 22 |
+
({"query": "asd"}),
|
| 23 |
+
({"query": "btc", "provider": "fmp"}),
|
| 24 |
+
],
|
| 25 |
+
)
|
| 26 |
+
@pytest.mark.integration
|
| 27 |
+
def test_crypto_search(params, obb):
|
| 28 |
+
"""Test the crypto search endpoint."""
|
| 29 |
+
params = {p: v for p, v in params.items() if v}
|
| 30 |
+
|
| 31 |
+
result = obb.crypto.search(**params)
|
| 32 |
+
assert result
|
| 33 |
+
assert isinstance(result, OBBject)
|
| 34 |
+
assert len(result.results) > 0
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
@parametrize(
|
| 38 |
+
"params",
|
| 39 |
+
[
|
| 40 |
+
(
|
| 41 |
+
{
|
| 42 |
+
"interval": "1d",
|
| 43 |
+
"provider": "fmp",
|
| 44 |
+
"symbol": "BTCUSD",
|
| 45 |
+
"start_date": "2023-01-01",
|
| 46 |
+
"end_date": "2023-01-02",
|
| 47 |
+
}
|
| 48 |
+
),
|
| 49 |
+
(
|
| 50 |
+
{
|
| 51 |
+
"interval": "1h",
|
| 52 |
+
"provider": "fmp",
|
| 53 |
+
"symbol": "BTCUSD,ETHUSD",
|
| 54 |
+
"start_date": None,
|
| 55 |
+
"end_date": None,
|
| 56 |
+
}
|
| 57 |
+
),
|
| 58 |
+
(
|
| 59 |
+
{
|
| 60 |
+
"interval": "1m",
|
| 61 |
+
"sort": "desc",
|
| 62 |
+
"limit": 49999,
|
| 63 |
+
"provider": "polygon",
|
| 64 |
+
"symbol": "BTCUSD",
|
| 65 |
+
"start_date": "2023-01-01",
|
| 66 |
+
"end_date": "2023-01-02",
|
| 67 |
+
}
|
| 68 |
+
),
|
| 69 |
+
(
|
| 70 |
+
{
|
| 71 |
+
"interval": "1d",
|
| 72 |
+
"sort": "desc",
|
| 73 |
+
"limit": 49999,
|
| 74 |
+
"provider": "polygon",
|
| 75 |
+
"symbol": "BTCUSD",
|
| 76 |
+
"start_date": "2023-01-01",
|
| 77 |
+
"end_date": "2023-06-06",
|
| 78 |
+
}
|
| 79 |
+
),
|
| 80 |
+
(
|
| 81 |
+
{
|
| 82 |
+
"interval": "1d",
|
| 83 |
+
"provider": "yfinance",
|
| 84 |
+
"symbol": "BTCUSD",
|
| 85 |
+
"start_date": "2023-01-01",
|
| 86 |
+
"end_date": "2023-01-04",
|
| 87 |
+
}
|
| 88 |
+
),
|
| 89 |
+
(
|
| 90 |
+
{
|
| 91 |
+
"provider": "tiingo",
|
| 92 |
+
"interval": "1d",
|
| 93 |
+
"exchanges": None,
|
| 94 |
+
"symbol": "BTCUSD",
|
| 95 |
+
"start_date": "2023-01-01",
|
| 96 |
+
"end_date": "2023-06-06",
|
| 97 |
+
}
|
| 98 |
+
),
|
| 99 |
+
(
|
| 100 |
+
{
|
| 101 |
+
"provider": "tiingo",
|
| 102 |
+
"interval": "1h",
|
| 103 |
+
"exchanges": ["POLONIEX", "GDAX"],
|
| 104 |
+
"symbol": "BTCUSD",
|
| 105 |
+
"start_date": "2023-01-01",
|
| 106 |
+
"end_date": "2023-01-02",
|
| 107 |
+
}
|
| 108 |
+
),
|
| 109 |
+
],
|
| 110 |
+
)
|
| 111 |
+
@pytest.mark.integration
|
| 112 |
+
def test_crypto_price_historical(params, obb):
|
| 113 |
+
"""Test crypto price historical."""
|
| 114 |
+
result = obb.crypto.price.historical(**params)
|
| 115 |
+
assert result
|
| 116 |
+
assert isinstance(result, OBBject)
|
| 117 |
+
assert len(result.results) > 0
|
openbb_platform/extensions/crypto/openbb_crypto/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""OpenBB Crypto Extension."""
|
openbb_platform/extensions/crypto/openbb_crypto/crypto_router.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Crypto Router."""
|
| 2 |
+
|
| 3 |
+
from openbb_core.app.model.command_context import CommandContext
|
| 4 |
+
from openbb_core.app.model.example import APIEx
|
| 5 |
+
from openbb_core.app.model.obbject import OBBject
|
| 6 |
+
from openbb_core.app.provider_interface import (
|
| 7 |
+
ExtraParams,
|
| 8 |
+
ProviderChoices,
|
| 9 |
+
StandardParams,
|
| 10 |
+
)
|
| 11 |
+
from openbb_core.app.query import Query
|
| 12 |
+
from openbb_core.app.router import Router
|
| 13 |
+
|
| 14 |
+
from openbb_crypto.price.price_router import router as price_router
|
| 15 |
+
|
| 16 |
+
router = Router(prefix="", description="Cryptocurrency market data.")
|
| 17 |
+
router.include_router(price_router)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
# pylint: disable=unused-argument
|
| 21 |
+
@router.command(
|
| 22 |
+
model="CryptoSearch",
|
| 23 |
+
examples=[
|
| 24 |
+
APIEx(parameters={"provider": "fmp"}),
|
| 25 |
+
APIEx(parameters={"query": "BTCUSD", "provider": "fmp"}),
|
| 26 |
+
],
|
| 27 |
+
)
|
| 28 |
+
async def search(
|
| 29 |
+
cc: CommandContext,
|
| 30 |
+
provider_choices: ProviderChoices,
|
| 31 |
+
standard_params: StandardParams,
|
| 32 |
+
extra_params: ExtraParams,
|
| 33 |
+
) -> OBBject:
|
| 34 |
+
"""Search available cryptocurrency pairs within a provider."""
|
| 35 |
+
return await OBBject.from_query(Query(**locals()))
|
openbb_platform/extensions/crypto/openbb_crypto/crypto_views.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Views for the crypto Extension."""
|
| 2 |
+
|
| 3 |
+
from typing import TYPE_CHECKING, Any, Dict, Tuple
|
| 4 |
+
|
| 5 |
+
if TYPE_CHECKING:
|
| 6 |
+
from openbb_charting.core.openbb_figure import (
|
| 7 |
+
OpenBBFigure,
|
| 8 |
+
)
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class CryptoViews:
|
| 12 |
+
"""Crypto Views."""
|
| 13 |
+
|
| 14 |
+
@staticmethod
|
| 15 |
+
def crypto_price_historical( # noqa: PLR0912
|
| 16 |
+
**kwargs,
|
| 17 |
+
) -> Tuple["OpenBBFigure", Dict[str, Any]]:
|
| 18 |
+
"""Crypto Price Historical Chart."""
|
| 19 |
+
# pylint: disable=import-outside-toplevel
|
| 20 |
+
from openbb_charting.charts.price_historical import price_historical
|
| 21 |
+
|
| 22 |
+
return price_historical(**kwargs)
|
openbb_platform/extensions/crypto/openbb_crypto/price/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""OpenBB Crypto Price Router."""
|
openbb_platform/extensions/crypto/openbb_crypto/price/price_router.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# pylint: disable=W0613:unused-argument
|
| 2 |
+
"""Crypto Price Router."""
|
| 3 |
+
|
| 4 |
+
from openbb_core.app.model.command_context import CommandContext
|
| 5 |
+
from openbb_core.app.model.example import APIEx
|
| 6 |
+
from openbb_core.app.model.obbject import OBBject
|
| 7 |
+
from openbb_core.app.provider_interface import (
|
| 8 |
+
ExtraParams,
|
| 9 |
+
ProviderChoices,
|
| 10 |
+
StandardParams,
|
| 11 |
+
)
|
| 12 |
+
from openbb_core.app.query import Query
|
| 13 |
+
from openbb_core.app.router import Router
|
| 14 |
+
|
| 15 |
+
router = Router(prefix="/price")
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
# pylint: disable=unused-argument,line-too-long
|
| 19 |
+
@router.command(
|
| 20 |
+
model="CryptoHistorical",
|
| 21 |
+
examples=[
|
| 22 |
+
APIEx(parameters={"symbol": "BTCUSD", "provider": "fmp"}),
|
| 23 |
+
APIEx(
|
| 24 |
+
parameters={
|
| 25 |
+
"symbol": "BTCUSD",
|
| 26 |
+
"start_date": "2024-01-01",
|
| 27 |
+
"end_date": "2024-01-31",
|
| 28 |
+
"provider": "fmp",
|
| 29 |
+
},
|
| 30 |
+
),
|
| 31 |
+
APIEx(
|
| 32 |
+
parameters={
|
| 33 |
+
"symbol": "BTCUSD,ETHUSD",
|
| 34 |
+
"start_date": "2024-01-01",
|
| 35 |
+
"end_date": "2024-01-31",
|
| 36 |
+
"provider": "polygon",
|
| 37 |
+
},
|
| 38 |
+
),
|
| 39 |
+
APIEx(
|
| 40 |
+
description="Get monthly historical prices from Yahoo Finance for Ethereum.",
|
| 41 |
+
parameters={
|
| 42 |
+
"symbol": "ETH-USD",
|
| 43 |
+
"interval": "1m",
|
| 44 |
+
"start_date": "2024-01-01",
|
| 45 |
+
"end_date": "2024-12-31",
|
| 46 |
+
"provider": "yfinance",
|
| 47 |
+
},
|
| 48 |
+
),
|
| 49 |
+
],
|
| 50 |
+
)
|
| 51 |
+
async def historical(
|
| 52 |
+
cc: CommandContext,
|
| 53 |
+
provider_choices: ProviderChoices,
|
| 54 |
+
standard_params: StandardParams,
|
| 55 |
+
extra_params: ExtraParams,
|
| 56 |
+
) -> OBBject:
|
| 57 |
+
"""Get historical price data for cryptocurrency pair(s) within a provider."""
|
| 58 |
+
return await OBBject.from_query(Query(**locals()))
|
openbb_platform/extensions/crypto/openbb_crypto/py.typed
ADDED
|
File without changes
|
openbb_platform/extensions/crypto/poetry.lock
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
openbb_platform/extensions/crypto/pyproject.toml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[tool.poetry]
|
| 2 |
+
name = "openbb-crypto"
|
| 3 |
+
version = "1.4.1"
|
| 4 |
+
description = "Crypto extension for OpenBB"
|
| 5 |
+
authors = ["OpenBB Team <hello@openbb.co>"]
|
| 6 |
+
license = "AGPL-3.0-only"
|
| 7 |
+
readme = "README.md"
|
| 8 |
+
packages = [{ include = "openbb_crypto" }]
|
| 9 |
+
|
| 10 |
+
[tool.poetry.dependencies]
|
| 11 |
+
python = ">=3.9.21,<3.13"
|
| 12 |
+
openbb-core = "^1.4.6"
|
| 13 |
+
|
| 14 |
+
[build-system]
|
| 15 |
+
requires = ["poetry-core"]
|
| 16 |
+
build-backend = "poetry.core.masonry.api"
|
| 17 |
+
|
| 18 |
+
[tool.poetry.plugins."openbb_core_extension"]
|
| 19 |
+
crypto = "openbb_crypto.crypto_router:router"
|
| 20 |
+
|
| 21 |
+
[tool.poetry.plugins."openbb_charting_extension"]
|
| 22 |
+
crypto = "openbb_crypto.crypto_views:CryptoViews"
|
openbb_platform/extensions/crypto/tests/.gitkeep
ADDED
|
File without changes
|
openbb_platform/extensions/currency/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# OpenBB Currency Extension
|
| 2 |
+
|
| 3 |
+
This extension provides currency exchange related data for the OpenBB Platform.
|
| 4 |
+
|
| 5 |
+
## Installation
|
| 6 |
+
|
| 7 |
+
To install the extension, run the following command in this folder:
|
| 8 |
+
|
| 9 |
+
```bash
|
| 10 |
+
pip install openbb-currency
|
| 11 |
+
```
|
| 12 |
+
|
| 13 |
+
Documentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).
|
openbb_platform/extensions/currency/integration/test_currency_api.py
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Test currency API endpoints."""
|
| 2 |
+
|
| 3 |
+
import base64
|
| 4 |
+
|
| 5 |
+
import pytest
|
| 6 |
+
import requests
|
| 7 |
+
from extensions.tests.conftest import parametrize
|
| 8 |
+
from openbb_core.env import Env
|
| 9 |
+
from openbb_core.provider.utils.helpers import get_querystring
|
| 10 |
+
|
| 11 |
+
# pylint: disable=redefined-outer-name
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
@pytest.fixture(scope="session")
|
| 15 |
+
def headers():
|
| 16 |
+
"""Get the headers for the API request."""
|
| 17 |
+
userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}"
|
| 18 |
+
userpass_bytes = userpass.encode("ascii")
|
| 19 |
+
base64_bytes = base64.b64encode(userpass_bytes)
|
| 20 |
+
|
| 21 |
+
return {"Authorization": f"Basic {base64_bytes.decode('ascii')}"}
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
@parametrize(
|
| 25 |
+
"params",
|
| 26 |
+
[
|
| 27 |
+
(
|
| 28 |
+
{
|
| 29 |
+
"provider": "polygon",
|
| 30 |
+
"query": "eur",
|
| 31 |
+
}
|
| 32 |
+
),
|
| 33 |
+
(
|
| 34 |
+
{
|
| 35 |
+
"provider": "fmp",
|
| 36 |
+
"query": "eur",
|
| 37 |
+
}
|
| 38 |
+
),
|
| 39 |
+
(
|
| 40 |
+
{
|
| 41 |
+
"provider": "intrinio",
|
| 42 |
+
"query": "eur",
|
| 43 |
+
}
|
| 44 |
+
),
|
| 45 |
+
],
|
| 46 |
+
)
|
| 47 |
+
@pytest.mark.integration
|
| 48 |
+
def test_currency_search(params, headers):
|
| 49 |
+
"""Test the currency search endpoint."""
|
| 50 |
+
params = {p: v for p, v in params.items() if v}
|
| 51 |
+
|
| 52 |
+
query_str = get_querystring(params, [])
|
| 53 |
+
url = f"http://0.0.0.0:8000/api/v1/currency/search?{query_str}"
|
| 54 |
+
result = requests.get(url, headers=headers, timeout=10)
|
| 55 |
+
assert isinstance(result, requests.Response)
|
| 56 |
+
assert result.status_code == 200
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
@parametrize(
|
| 60 |
+
"params",
|
| 61 |
+
[
|
| 62 |
+
(
|
| 63 |
+
{
|
| 64 |
+
"symbol": "EURUSD",
|
| 65 |
+
"interval": "1d",
|
| 66 |
+
"start_date": "2023-01-01",
|
| 67 |
+
"end_date": "2023-06-06",
|
| 68 |
+
"provider": "fmp",
|
| 69 |
+
}
|
| 70 |
+
),
|
| 71 |
+
(
|
| 72 |
+
{
|
| 73 |
+
"interval": "1h",
|
| 74 |
+
"provider": "fmp",
|
| 75 |
+
"symbol": "EURUSD,USDJPY",
|
| 76 |
+
"start_date": None,
|
| 77 |
+
"end_date": None,
|
| 78 |
+
}
|
| 79 |
+
),
|
| 80 |
+
(
|
| 81 |
+
{
|
| 82 |
+
"interval": "1m",
|
| 83 |
+
"sort": "desc",
|
| 84 |
+
"limit": 49999,
|
| 85 |
+
"provider": "polygon",
|
| 86 |
+
"symbol": "EURUSD",
|
| 87 |
+
"start_date": "2023-01-01",
|
| 88 |
+
"end_date": "2023-01-10",
|
| 89 |
+
}
|
| 90 |
+
),
|
| 91 |
+
(
|
| 92 |
+
{
|
| 93 |
+
"interval": "1d",
|
| 94 |
+
"sort": "desc",
|
| 95 |
+
"limit": 49999,
|
| 96 |
+
"provider": "polygon",
|
| 97 |
+
"symbol": "EURUSD",
|
| 98 |
+
"start_date": "2023-01-01",
|
| 99 |
+
"end_date": "2023-06-06",
|
| 100 |
+
}
|
| 101 |
+
),
|
| 102 |
+
(
|
| 103 |
+
{
|
| 104 |
+
"interval": "1d",
|
| 105 |
+
"provider": "yfinance",
|
| 106 |
+
"symbol": "EURUSD",
|
| 107 |
+
"start_date": "2023-01-01",
|
| 108 |
+
"end_date": "2023-01-10",
|
| 109 |
+
}
|
| 110 |
+
),
|
| 111 |
+
(
|
| 112 |
+
{
|
| 113 |
+
"interval": "1m",
|
| 114 |
+
"provider": "yfinance",
|
| 115 |
+
"symbol": "EURUSD",
|
| 116 |
+
"start_date": None,
|
| 117 |
+
"end_date": None,
|
| 118 |
+
}
|
| 119 |
+
),
|
| 120 |
+
(
|
| 121 |
+
{
|
| 122 |
+
"interval": "1h",
|
| 123 |
+
"provider": "tiingo",
|
| 124 |
+
"symbol": "EURUSD",
|
| 125 |
+
"start_date": "2023-05-21",
|
| 126 |
+
"end_date": "2023-06-06",
|
| 127 |
+
}
|
| 128 |
+
),
|
| 129 |
+
(
|
| 130 |
+
{
|
| 131 |
+
"interval": "1d",
|
| 132 |
+
"provider": "tiingo",
|
| 133 |
+
"symbol": "EURUSD",
|
| 134 |
+
"start_date": "2023-05-21",
|
| 135 |
+
"end_date": "2023-06-06",
|
| 136 |
+
}
|
| 137 |
+
),
|
| 138 |
+
],
|
| 139 |
+
)
|
| 140 |
+
@pytest.mark.integration
|
| 141 |
+
def test_currency_price_historical(params, headers):
|
| 142 |
+
"""Test the currency historical price endpoint."""
|
| 143 |
+
params = {p: v for p, v in params.items() if v}
|
| 144 |
+
|
| 145 |
+
query_str = get_querystring(params, [])
|
| 146 |
+
url = f"http://0.0.0.0:8000/api/v1/currency/price/historical?{query_str}"
|
| 147 |
+
result = requests.get(url, headers=headers, timeout=10)
|
| 148 |
+
assert isinstance(result, requests.Response)
|
| 149 |
+
assert result.status_code == 200
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
@parametrize(
|
| 153 |
+
"params",
|
| 154 |
+
[({"provider": "ecb"})],
|
| 155 |
+
)
|
| 156 |
+
@pytest.mark.integration
|
| 157 |
+
def test_currency_reference_rates(params, headers):
|
| 158 |
+
"""Test the currency reference rates endpoint."""
|
| 159 |
+
params = {p: v for p, v in params.items() if v}
|
| 160 |
+
|
| 161 |
+
query_str = get_querystring(params, [])
|
| 162 |
+
url = f"http://0.0.0.0:8000/api/v1/currency/reference_rates?{query_str}"
|
| 163 |
+
result = requests.get(url, headers=headers, timeout=10)
|
| 164 |
+
assert isinstance(result, requests.Response)
|
| 165 |
+
assert result.status_code == 200
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
@parametrize(
|
| 169 |
+
"params",
|
| 170 |
+
[
|
| 171 |
+
(
|
| 172 |
+
{
|
| 173 |
+
"provider": "fmp",
|
| 174 |
+
"base": "USD,XAU",
|
| 175 |
+
"counter_currencies": "EUR,JPY,GBP",
|
| 176 |
+
"quote_type": "indirect",
|
| 177 |
+
}
|
| 178 |
+
),
|
| 179 |
+
(
|
| 180 |
+
{
|
| 181 |
+
"provider": "polygon",
|
| 182 |
+
"base": "USD,XAU",
|
| 183 |
+
"counter_currencies": "EUR,JPY,GBP",
|
| 184 |
+
"quote_type": "indirect",
|
| 185 |
+
}
|
| 186 |
+
),
|
| 187 |
+
],
|
| 188 |
+
)
|
| 189 |
+
@pytest.mark.integration
|
| 190 |
+
def test_currency_snapshots(params, headers):
|
| 191 |
+
"""Test the currency snapshots endpoint."""
|
| 192 |
+
params = {p: v for p, v in params.items() if v}
|
| 193 |
+
|
| 194 |
+
query_str = get_querystring(params, [])
|
| 195 |
+
url = f"http://0.0.0.0:8000/api/v1/currency/snapshots?{query_str}"
|
| 196 |
+
result = requests.get(url, headers=headers, timeout=10)
|
| 197 |
+
assert isinstance(result, requests.Response)
|
| 198 |
+
assert result.status_code == 200
|
openbb_platform/extensions/currency/integration/test_currency_python.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Test currency extension."""
|
| 2 |
+
|
| 3 |
+
import pytest
|
| 4 |
+
from extensions.tests.conftest import parametrize
|
| 5 |
+
from openbb_core.app.model.obbject import OBBject
|
| 6 |
+
|
| 7 |
+
# pylint: disable=redefined-outer-name
|
| 8 |
+
# pylint: disable=inconsistent-return-statements
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@pytest.fixture(scope="session")
|
| 12 |
+
def obb(pytestconfig):
|
| 13 |
+
"""Fixture to setup obb."""
|
| 14 |
+
|
| 15 |
+
if pytestconfig.getoption("markexpr") != "not integration":
|
| 16 |
+
import openbb # pylint: disable=import-outside-toplevel
|
| 17 |
+
|
| 18 |
+
return openbb.obb
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
@parametrize(
|
| 22 |
+
"params",
|
| 23 |
+
[
|
| 24 |
+
(
|
| 25 |
+
{
|
| 26 |
+
"provider": "polygon",
|
| 27 |
+
"query": "eur",
|
| 28 |
+
}
|
| 29 |
+
),
|
| 30 |
+
(
|
| 31 |
+
{
|
| 32 |
+
"provider": "fmp",
|
| 33 |
+
"query": "eur",
|
| 34 |
+
}
|
| 35 |
+
),
|
| 36 |
+
(
|
| 37 |
+
{
|
| 38 |
+
"provider": "intrinio",
|
| 39 |
+
"query": "eur",
|
| 40 |
+
}
|
| 41 |
+
),
|
| 42 |
+
],
|
| 43 |
+
)
|
| 44 |
+
@pytest.mark.integration
|
| 45 |
+
def test_currency_search(params, obb):
|
| 46 |
+
"""Test the currency search endpoint."""
|
| 47 |
+
result = obb.currency.search(**params)
|
| 48 |
+
assert result
|
| 49 |
+
assert isinstance(result, OBBject)
|
| 50 |
+
assert len(result.results) > 0
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
@parametrize(
|
| 54 |
+
"params",
|
| 55 |
+
[
|
| 56 |
+
(
|
| 57 |
+
{
|
| 58 |
+
"symbol": "EURUSD",
|
| 59 |
+
"interval": "1d",
|
| 60 |
+
"start_date": "2023-01-01",
|
| 61 |
+
"end_date": "2023-06-06",
|
| 62 |
+
"provider": "fmp",
|
| 63 |
+
}
|
| 64 |
+
),
|
| 65 |
+
(
|
| 66 |
+
{
|
| 67 |
+
"interval": "1h",
|
| 68 |
+
"provider": "fmp",
|
| 69 |
+
"symbol": "EURUSD,USDJPY",
|
| 70 |
+
"start_date": None,
|
| 71 |
+
"end_date": None,
|
| 72 |
+
}
|
| 73 |
+
),
|
| 74 |
+
(
|
| 75 |
+
{
|
| 76 |
+
"interval": "1m",
|
| 77 |
+
"sort": "desc",
|
| 78 |
+
"limit": 49999,
|
| 79 |
+
"provider": "polygon",
|
| 80 |
+
"symbol": "EURUSD",
|
| 81 |
+
"start_date": "2023-01-01",
|
| 82 |
+
"end_date": "2023-01-10",
|
| 83 |
+
}
|
| 84 |
+
),
|
| 85 |
+
(
|
| 86 |
+
{
|
| 87 |
+
"interval": "1d",
|
| 88 |
+
"sort": "desc",
|
| 89 |
+
"limit": 49999,
|
| 90 |
+
"provider": "polygon",
|
| 91 |
+
"symbol": "EURUSD",
|
| 92 |
+
"start_date": "2023-01-01",
|
| 93 |
+
"end_date": "2023-06-06",
|
| 94 |
+
}
|
| 95 |
+
),
|
| 96 |
+
(
|
| 97 |
+
{
|
| 98 |
+
"interval": "1d",
|
| 99 |
+
"provider": "yfinance",
|
| 100 |
+
"symbol": "EURUSD",
|
| 101 |
+
"start_date": "2023-01-01",
|
| 102 |
+
"end_date": "2023-01-10",
|
| 103 |
+
}
|
| 104 |
+
),
|
| 105 |
+
(
|
| 106 |
+
{
|
| 107 |
+
"interval": "1m",
|
| 108 |
+
"provider": "yfinance",
|
| 109 |
+
"symbol": "EURUSD",
|
| 110 |
+
"start_date": None,
|
| 111 |
+
"end_date": None,
|
| 112 |
+
}
|
| 113 |
+
),
|
| 114 |
+
(
|
| 115 |
+
{
|
| 116 |
+
"interval": "1h",
|
| 117 |
+
"provider": "tiingo",
|
| 118 |
+
"symbol": "EURUSD",
|
| 119 |
+
"start_date": "2023-05-21",
|
| 120 |
+
"end_date": "2023-06-06",
|
| 121 |
+
}
|
| 122 |
+
),
|
| 123 |
+
(
|
| 124 |
+
{
|
| 125 |
+
"interval": "1d",
|
| 126 |
+
"provider": "tiingo",
|
| 127 |
+
"symbol": "EURUSD",
|
| 128 |
+
"start_date": "2023-05-21",
|
| 129 |
+
"end_date": "2023-06-06",
|
| 130 |
+
}
|
| 131 |
+
),
|
| 132 |
+
],
|
| 133 |
+
)
|
| 134 |
+
@pytest.mark.integration
|
| 135 |
+
def test_currency_price_historical(params, obb):
|
| 136 |
+
"""Test the currency historical price endpoint."""
|
| 137 |
+
result = obb.currency.price.historical(**params)
|
| 138 |
+
assert result
|
| 139 |
+
assert isinstance(result, OBBject)
|
| 140 |
+
assert len(result.results) > 0
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
@parametrize(
|
| 144 |
+
"params",
|
| 145 |
+
[({"provider": "ecb"})],
|
| 146 |
+
)
|
| 147 |
+
@pytest.mark.integration
|
| 148 |
+
def test_currency_reference_rates(params, obb):
|
| 149 |
+
"""Test the currency reference rates endpoint."""
|
| 150 |
+
result = obb.currency.reference_rates(**params)
|
| 151 |
+
assert result
|
| 152 |
+
assert isinstance(result, OBBject)
|
| 153 |
+
assert len(result.model_dump()["results"].items()) > 0
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
@parametrize(
|
| 157 |
+
"params",
|
| 158 |
+
[
|
| 159 |
+
(
|
| 160 |
+
{
|
| 161 |
+
"provider": "fmp",
|
| 162 |
+
"base": "USD,XAU",
|
| 163 |
+
"counter_currencies": "EUR,JPY,GBP",
|
| 164 |
+
"quote_type": "indirect",
|
| 165 |
+
}
|
| 166 |
+
),
|
| 167 |
+
(
|
| 168 |
+
{
|
| 169 |
+
"provider": "polygon",
|
| 170 |
+
"base": "USD,XAU",
|
| 171 |
+
"counter_currencies": "EUR,JPY,GBP",
|
| 172 |
+
"quote_type": "indirect",
|
| 173 |
+
}
|
| 174 |
+
),
|
| 175 |
+
],
|
| 176 |
+
)
|
| 177 |
+
@pytest.mark.integration
|
| 178 |
+
def test_currency_snapshots(params, obb):
|
| 179 |
+
"""Test the currency snapshots endpoint."""
|
| 180 |
+
result = obb.currency.snapshots(**params)
|
| 181 |
+
assert result
|
| 182 |
+
assert isinstance(result, OBBject)
|
| 183 |
+
assert len(result.results) > 0
|
openbb_platform/extensions/currency/openbb_currency/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""The Currency router init."""
|
openbb_platform/extensions/currency/openbb_currency/currency_router.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""The Currency router."""
|
| 2 |
+
|
| 3 |
+
from openbb_core.app.model.command_context import CommandContext
|
| 4 |
+
from openbb_core.app.model.example import APIEx
|
| 5 |
+
from openbb_core.app.model.obbject import OBBject
|
| 6 |
+
from openbb_core.app.provider_interface import (
|
| 7 |
+
ExtraParams,
|
| 8 |
+
ProviderChoices,
|
| 9 |
+
StandardParams,
|
| 10 |
+
)
|
| 11 |
+
from openbb_core.app.query import Query
|
| 12 |
+
from openbb_core.app.router import Router
|
| 13 |
+
|
| 14 |
+
from openbb_currency.price.price_router import router as price_router
|
| 15 |
+
|
| 16 |
+
router = Router(prefix="", description="Foreign exchange (FX) market data.")
|
| 17 |
+
router.include_router(price_router)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
# pylint: disable=unused-argument
|
| 21 |
+
@router.command(
|
| 22 |
+
model="CurrencyPairs",
|
| 23 |
+
examples=[
|
| 24 |
+
APIEx(parameters={"provider": "fmp"}),
|
| 25 |
+
APIEx(
|
| 26 |
+
description="Search for 'EUR' currency pair using 'intrinio' as provider.",
|
| 27 |
+
parameters={"provider": "intrinio", "query": "EUR"},
|
| 28 |
+
),
|
| 29 |
+
APIEx(
|
| 30 |
+
description="Search for terms using 'polygon' as provider.",
|
| 31 |
+
parameters={"provider": "polygon", "query": "EUR"},
|
| 32 |
+
),
|
| 33 |
+
],
|
| 34 |
+
)
|
| 35 |
+
async def search(
|
| 36 |
+
cc: CommandContext,
|
| 37 |
+
provider_choices: ProviderChoices,
|
| 38 |
+
standard_params: StandardParams,
|
| 39 |
+
extra_params: ExtraParams,
|
| 40 |
+
) -> OBBject:
|
| 41 |
+
"""Currency Search.
|
| 42 |
+
|
| 43 |
+
Search available currency pairs.
|
| 44 |
+
Currency pairs are the national currencies from two countries coupled for trading on
|
| 45 |
+
the foreign exchange (FX) marketplace.
|
| 46 |
+
Both currencies will have exchange rates on which the trade will have its position basis.
|
| 47 |
+
All trading within the forex market, whether selling, buying, or trading, will take place through currency pairs.
|
| 48 |
+
(ref: Investopedia)
|
| 49 |
+
Major currency pairs include pairs such as EUR/USD, USD/JPY, GBP/USD, etc.
|
| 50 |
+
"""
|
| 51 |
+
return await OBBject.from_query(Query(**locals()))
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
@router.command(
|
| 55 |
+
model="CurrencyReferenceRates",
|
| 56 |
+
examples=[APIEx(parameters={"provider": "ecb"})],
|
| 57 |
+
)
|
| 58 |
+
async def reference_rates(
|
| 59 |
+
cc: CommandContext,
|
| 60 |
+
provider_choices: ProviderChoices,
|
| 61 |
+
standard_params: StandardParams,
|
| 62 |
+
extra_params: ExtraParams,
|
| 63 |
+
) -> OBBject:
|
| 64 |
+
"""Get current, official, currency reference rates.
|
| 65 |
+
|
| 66 |
+
Foreign exchange reference rates are the exchange rates set by a major financial institution or regulatory body,
|
| 67 |
+
serving as a benchmark for the value of currencies around the world.
|
| 68 |
+
These rates are used as a standard to facilitate international trade and financial transactions,
|
| 69 |
+
ensuring consistency and reliability in currency conversion.
|
| 70 |
+
They are typically updated on a daily basis and reflect the market conditions at a specific time.
|
| 71 |
+
Central banks and financial institutions often use these rates to guide their own exchange rates,
|
| 72 |
+
impacting global trade, loans, and investments.
|
| 73 |
+
"""
|
| 74 |
+
return await OBBject.from_query(Query(**locals()))
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
@router.command(
|
| 78 |
+
model="CurrencySnapshots",
|
| 79 |
+
examples=[
|
| 80 |
+
APIEx(parameters={"provider": "fmp"}),
|
| 81 |
+
APIEx(
|
| 82 |
+
description="Get exchange rates from USD and XAU to EUR, JPY, and GBP using 'fmp' as provider.",
|
| 83 |
+
parameters={
|
| 84 |
+
"provider": "fmp",
|
| 85 |
+
"base": "USD,XAU",
|
| 86 |
+
"counter_currencies": "EUR,JPY,GBP",
|
| 87 |
+
"quote_type": "indirect",
|
| 88 |
+
},
|
| 89 |
+
),
|
| 90 |
+
],
|
| 91 |
+
)
|
| 92 |
+
async def snapshots(
|
| 93 |
+
cc: CommandContext,
|
| 94 |
+
provider_choices: ProviderChoices,
|
| 95 |
+
standard_params: StandardParams,
|
| 96 |
+
extra_params: ExtraParams,
|
| 97 |
+
) -> OBBject:
|
| 98 |
+
"""Snapshots of currency exchange rates from an indirect or direct perspective of a base currency."""
|
| 99 |
+
return await OBBject.from_query(Query(**locals()))
|
openbb_platform/extensions/currency/openbb_currency/currency_views.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Views for the Currency Extension."""
|
| 2 |
+
|
| 3 |
+
from typing import TYPE_CHECKING, Any, Dict, Tuple
|
| 4 |
+
|
| 5 |
+
if TYPE_CHECKING:
|
| 6 |
+
from openbb_charting.core.openbb_figure import (
|
| 7 |
+
OpenBBFigure,
|
| 8 |
+
)
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class CurrencyViews:
|
| 12 |
+
"""Currency Views."""
|
| 13 |
+
|
| 14 |
+
@staticmethod
|
| 15 |
+
def currency_price_historical( # noqa: PLR0912
|
| 16 |
+
**kwargs,
|
| 17 |
+
) -> Tuple["OpenBBFigure", Dict[str, Any]]:
|
| 18 |
+
"""Currency Price Historical Chart."""
|
| 19 |
+
# pylint: disable=import-outside-toplevel
|
| 20 |
+
from openbb_charting.charts.price_historical import price_historical
|
| 21 |
+
|
| 22 |
+
return price_historical(**kwargs)
|
openbb_platform/extensions/currency/openbb_currency/price/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""The Currency price router init."""
|
openbb_platform/extensions/currency/openbb_currency/price/price_router.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Price router for Currency."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
from openbb_core.app.model.command_context import CommandContext
|
| 5 |
+
from openbb_core.app.model.example import APIEx
|
| 6 |
+
from openbb_core.app.model.obbject import OBBject
|
| 7 |
+
from openbb_core.app.provider_interface import (
|
| 8 |
+
ExtraParams,
|
| 9 |
+
ProviderChoices,
|
| 10 |
+
StandardParams,
|
| 11 |
+
)
|
| 12 |
+
from openbb_core.app.query import Query
|
| 13 |
+
from openbb_core.app.router import Router
|
| 14 |
+
|
| 15 |
+
router = Router(prefix="/price")
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
# pylint: disable=unused-argument
|
| 19 |
+
@router.command(
|
| 20 |
+
model="CurrencyHistorical",
|
| 21 |
+
examples=[
|
| 22 |
+
APIEx(parameters={"symbol": "EURUSD", "provider": "fmp"}),
|
| 23 |
+
APIEx(
|
| 24 |
+
description="Filter historical data with specific start and end date.",
|
| 25 |
+
parameters={
|
| 26 |
+
"symbol": "EURUSD",
|
| 27 |
+
"start_date": "2023-01-01",
|
| 28 |
+
"end_date": "2023-12-31",
|
| 29 |
+
"provider": "fmp",
|
| 30 |
+
},
|
| 31 |
+
),
|
| 32 |
+
APIEx(
|
| 33 |
+
description="Get data with different granularity.",
|
| 34 |
+
parameters={"symbol": "EURUSD", "provider": "polygon", "interval": "15m"},
|
| 35 |
+
),
|
| 36 |
+
],
|
| 37 |
+
)
|
| 38 |
+
async def historical(
|
| 39 |
+
cc: CommandContext,
|
| 40 |
+
provider_choices: ProviderChoices,
|
| 41 |
+
standard_params: StandardParams,
|
| 42 |
+
extra_params: ExtraParams,
|
| 43 |
+
) -> OBBject:
|
| 44 |
+
"""
|
| 45 |
+
Currency Historical Price. Currency historical data.
|
| 46 |
+
|
| 47 |
+
Currency historical prices refer to the past exchange rates of one currency against
|
| 48 |
+
another over a specific period.
|
| 49 |
+
This data provides insight into the fluctuations and trends in the foreign exchange market,
|
| 50 |
+
helping analysts, traders, and economists understand currency performance,
|
| 51 |
+
evaluate economic health, and make predictions about future movements.
|
| 52 |
+
"""
|
| 53 |
+
return await OBBject.from_query(Query(**locals()))
|
openbb_platform/extensions/currency/openbb_currency/py.typed
ADDED
|
File without changes
|
openbb_platform/extensions/currency/poetry.lock
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
openbb_platform/extensions/currency/pyproject.toml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[tool.poetry]
|
| 2 |
+
name = "openbb-currency"
|
| 3 |
+
version = "1.4.1"
|
| 4 |
+
description = "Currency extension for OpenBB"
|
| 5 |
+
authors = ["OpenBB Team <hello@openbb.co>"]
|
| 6 |
+
license = "AGPL-3.0-only"
|
| 7 |
+
readme = "README.md"
|
| 8 |
+
packages = [{ include = "openbb_currency" }]
|
| 9 |
+
|
| 10 |
+
[tool.poetry.dependencies]
|
| 11 |
+
python = ">=3.9.21,<3.13"
|
| 12 |
+
openbb-core = "^1.4.6"
|
| 13 |
+
|
| 14 |
+
[build-system]
|
| 15 |
+
requires = ["poetry-core"]
|
| 16 |
+
build-backend = "poetry.core.masonry.api"
|
| 17 |
+
|
| 18 |
+
[tool.poetry.plugins."openbb_core_extension"]
|
| 19 |
+
currency = "openbb_currency.currency_router:router"
|
| 20 |
+
|
| 21 |
+
[tool.poetry.plugins."openbb_charting_extension"]
|
| 22 |
+
currency = "openbb_currency.currency_views:CurrencyViews"
|
openbb_platform/extensions/currency/tests/.gitkeep
ADDED
|
File without changes
|
openbb_platform/extensions/derivatives/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# OpenBB Derivatives Extension
|
| 2 |
+
|
| 3 |
+
This extension provides derivatives data for the OpenBB Platform.
|
| 4 |
+
|
| 5 |
+
## Installation
|
| 6 |
+
|
| 7 |
+
To install the extension, run the following command in this folder:
|
| 8 |
+
|
| 9 |
+
```bash
|
| 10 |
+
pip install openbb-derivatives
|
| 11 |
+
```
|
| 12 |
+
|
| 13 |
+
Documentation available [here](https://docs.openbb.co/sdk).
|
openbb_platform/extensions/derivatives/integration/test_derivatives_api.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""API integration tests for the derivatives extension."""
|
| 2 |
+
|
| 3 |
+
import base64
|
| 4 |
+
|
| 5 |
+
import pytest
|
| 6 |
+
import requests
|
| 7 |
+
from extensions.tests.conftest import parametrize
|
| 8 |
+
from openbb_core.env import Env
|
| 9 |
+
from openbb_core.provider.utils.helpers import get_querystring
|
| 10 |
+
|
| 11 |
+
# pylint: disable=too-many-lines,redefined-outer-name
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
@pytest.fixture(scope="session")
|
| 15 |
+
def headers():
|
| 16 |
+
"""Get the headers for the API request."""
|
| 17 |
+
userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}"
|
| 18 |
+
userpass_bytes = userpass.encode("ascii")
|
| 19 |
+
base64_bytes = base64.b64encode(userpass_bytes)
|
| 20 |
+
|
| 21 |
+
return {"Authorization": f"Basic {base64_bytes.decode('ascii')}"}
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
@parametrize(
|
| 25 |
+
"params",
|
| 26 |
+
[
|
| 27 |
+
(
|
| 28 |
+
{
|
| 29 |
+
"provider": "intrinio",
|
| 30 |
+
"symbol": "AAPL",
|
| 31 |
+
"date": "2023-01-25",
|
| 32 |
+
"option_type": None,
|
| 33 |
+
"moneyness": "all",
|
| 34 |
+
"strike_gt": None,
|
| 35 |
+
"strike_lt": None,
|
| 36 |
+
"volume_gt": None,
|
| 37 |
+
"volume_lt": None,
|
| 38 |
+
"oi_gt": None,
|
| 39 |
+
"oi_lt": None,
|
| 40 |
+
"model": "black_scholes",
|
| 41 |
+
"show_extended_price": False,
|
| 42 |
+
"include_related_symbols": False,
|
| 43 |
+
"delay": "delayed",
|
| 44 |
+
}
|
| 45 |
+
),
|
| 46 |
+
({"provider": "cboe", "symbol": "AAPL", "use_cache": False}),
|
| 47 |
+
({"provider": "tradier", "symbol": "AAPL"}),
|
| 48 |
+
({"provider": "yfinance", "symbol": "AAPL"}),
|
| 49 |
+
({"provider": "deribit", "symbol": "BTC"}),
|
| 50 |
+
(
|
| 51 |
+
{
|
| 52 |
+
"provider": "tmx",
|
| 53 |
+
"symbol": "SHOP",
|
| 54 |
+
"date": "2022-12-28",
|
| 55 |
+
"use_cache": False,
|
| 56 |
+
}
|
| 57 |
+
),
|
| 58 |
+
],
|
| 59 |
+
)
|
| 60 |
+
@pytest.mark.integration
|
| 61 |
+
def test_derivatives_options_chains(params, headers):
|
| 62 |
+
"""Test the options chains endpoint."""
|
| 63 |
+
params = {p: v for p, v in params.items() if v}
|
| 64 |
+
|
| 65 |
+
query_str = get_querystring(params, [])
|
| 66 |
+
url = f"http://0.0.0.0:8000/api/v1/derivatives/options/chains?{query_str}"
|
| 67 |
+
result = requests.get(url, headers=headers, timeout=10)
|
| 68 |
+
assert isinstance(result, requests.Response)
|
| 69 |
+
assert result.status_code == 200
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
@parametrize(
|
| 73 |
+
"params",
|
| 74 |
+
[
|
| 75 |
+
(
|
| 76 |
+
{
|
| 77 |
+
"symbol": "AAPL",
|
| 78 |
+
"provider": "intrinio",
|
| 79 |
+
"start_date": "2023-11-20",
|
| 80 |
+
"end_date": None,
|
| 81 |
+
"min_value": None,
|
| 82 |
+
"max_value": None,
|
| 83 |
+
"trade_type": None,
|
| 84 |
+
"sentiment": "neutral",
|
| 85 |
+
"limit": 1000,
|
| 86 |
+
"source": "delayed",
|
| 87 |
+
}
|
| 88 |
+
)
|
| 89 |
+
],
|
| 90 |
+
)
|
| 91 |
+
@pytest.mark.integration
|
| 92 |
+
def test_derivatives_options_unusual(params, headers):
|
| 93 |
+
"""Test the unusual options endpoint."""
|
| 94 |
+
params = {p: v for p, v in params.items() if v}
|
| 95 |
+
|
| 96 |
+
query_str = get_querystring(params, [])
|
| 97 |
+
url = f"http://0.0.0.0:8000/api/v1/derivatives/options/unusual?{query_str}"
|
| 98 |
+
result = requests.get(url, headers=headers, timeout=10)
|
| 99 |
+
assert isinstance(result, requests.Response)
|
| 100 |
+
assert result.status_code == 200
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
@parametrize(
|
| 104 |
+
"params",
|
| 105 |
+
[
|
| 106 |
+
(
|
| 107 |
+
{
|
| 108 |
+
"provider": "yfinance",
|
| 109 |
+
"interval": "1d",
|
| 110 |
+
"symbol": "CL,BZ",
|
| 111 |
+
"start_date": "2023-01-01",
|
| 112 |
+
"end_date": "2023-06-06",
|
| 113 |
+
"expiration": "2025-12",
|
| 114 |
+
}
|
| 115 |
+
),
|
| 116 |
+
(
|
| 117 |
+
{
|
| 118 |
+
"provider": "deribit",
|
| 119 |
+
"interval": "1d",
|
| 120 |
+
"symbol": "BTC,ETH",
|
| 121 |
+
"start_date": "2023-01-01",
|
| 122 |
+
"end_date": "2023-06-06",
|
| 123 |
+
}
|
| 124 |
+
),
|
| 125 |
+
],
|
| 126 |
+
)
|
| 127 |
+
@pytest.mark.integration
|
| 128 |
+
def test_derivatives_futures_historical(params, headers):
|
| 129 |
+
"""Test the futures historical endpoint."""
|
| 130 |
+
params = {p: v for p, v in params.items() if v}
|
| 131 |
+
|
| 132 |
+
query_str = get_querystring(params, [])
|
| 133 |
+
url = f"http://0.0.0.0:8000/api/v1/derivatives/futures/historical?{query_str}"
|
| 134 |
+
result = requests.get(url, headers=headers, timeout=10)
|
| 135 |
+
assert isinstance(result, requests.Response)
|
| 136 |
+
assert result.status_code == 200
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
@parametrize(
|
| 140 |
+
"params",
|
| 141 |
+
[
|
| 142 |
+
(
|
| 143 |
+
{
|
| 144 |
+
"provider": "yfinance",
|
| 145 |
+
"symbol": "ES",
|
| 146 |
+
"date": None,
|
| 147 |
+
}
|
| 148 |
+
),
|
| 149 |
+
(
|
| 150 |
+
{
|
| 151 |
+
"provider": "cboe",
|
| 152 |
+
"symbol": "VX_EOD",
|
| 153 |
+
"date": "2024-06-25",
|
| 154 |
+
}
|
| 155 |
+
),
|
| 156 |
+
({"provider": "deribit", "date": None, "symbol": "BTC", "hours_ago": 12}),
|
| 157 |
+
],
|
| 158 |
+
)
|
| 159 |
+
@pytest.mark.integration
|
| 160 |
+
def test_derivatives_futures_curve(params, headers):
|
| 161 |
+
"""Test the futures curve endpoint."""
|
| 162 |
+
params = {p: v for p, v in params.items() if v}
|
| 163 |
+
|
| 164 |
+
query_str = get_querystring(params, [])
|
| 165 |
+
url = f"http://0.0.0.0:8000/api/v1/derivatives/futures/curve?{query_str}"
|
| 166 |
+
result = requests.get(url, headers=headers, timeout=10)
|
| 167 |
+
assert isinstance(result, requests.Response)
|
| 168 |
+
assert result.status_code == 200
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
@parametrize(
|
| 172 |
+
"params",
|
| 173 |
+
[
|
| 174 |
+
({"provider": "intrinio", "date": None, "only_traded": True}),
|
| 175 |
+
],
|
| 176 |
+
)
|
| 177 |
+
@pytest.mark.skip(
|
| 178 |
+
reason="This test is skipped because the download is excessively large."
|
| 179 |
+
)
|
| 180 |
+
def test_derivatives_options_snapshots(params, headers):
|
| 181 |
+
"""Test the options snapshots endpoint."""
|
| 182 |
+
params = {p: v for p, v in params.items() if v}
|
| 183 |
+
|
| 184 |
+
query_str = get_querystring(params, [])
|
| 185 |
+
url = f"http://0.0.0.0:8000/api/v1/derivatives/options/snapshots?{query_str}"
|
| 186 |
+
result = requests.get(url, headers=headers, timeout=60)
|
| 187 |
+
assert isinstance(result, requests.Response)
|
| 188 |
+
assert result.status_code == 200
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
@parametrize(
|
| 192 |
+
"params",
|
| 193 |
+
[
|
| 194 |
+
({"provider": "deribit"}),
|
| 195 |
+
],
|
| 196 |
+
)
|
| 197 |
+
@pytest.mark.integration
|
| 198 |
+
def test_derivatives_futures_instruments(params, headers):
|
| 199 |
+
"""Test the futures instruments endpoint."""
|
| 200 |
+
params = {p: v for p, v in params.items() if v}
|
| 201 |
+
|
| 202 |
+
query_str = get_querystring(params, [])
|
| 203 |
+
url = f"http://0.0.0.0:8000/api/v1/derivatives/futures/instruments?{query_str}"
|
| 204 |
+
result = requests.get(url, headers=headers, timeout=10)
|
| 205 |
+
assert isinstance(result, requests.Response)
|
| 206 |
+
assert result.status_code == 200
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
@parametrize(
|
| 210 |
+
"params",
|
| 211 |
+
[
|
| 212 |
+
({"provider": "deribit", "symbol": "ETH-PERPETUAL"}),
|
| 213 |
+
],
|
| 214 |
+
)
|
| 215 |
+
@pytest.mark.integration
|
| 216 |
+
def test_derivatives_futures_info(params, headers):
|
| 217 |
+
"""Test the futures info endpoint."""
|
| 218 |
+
params = {p: v for p, v in params.items() if v}
|
| 219 |
+
|
| 220 |
+
query_str = get_querystring(params, [])
|
| 221 |
+
url = f"http://0.0.0.0:8000/api/v1/derivatives/futures/info?{query_str}"
|
| 222 |
+
result = requests.get(url, headers=headers, timeout=10)
|
| 223 |
+
assert isinstance(result, requests.Response)
|
| 224 |
+
assert result.status_code == 200
|
openbb_platform/extensions/derivatives/integration/test_derivatives_python.py
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Python interface integration tests for the derivatives extension."""
|
| 2 |
+
|
| 3 |
+
import pytest
|
| 4 |
+
from extensions.tests.conftest import parametrize
|
| 5 |
+
from openbb_core.app.model.obbject import OBBject
|
| 6 |
+
|
| 7 |
+
# pylint: disable=too-many-lines,redefined-outer-name
|
| 8 |
+
# pylint: disable=import-outside-toplevel,inconsistent-return-statements
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@pytest.fixture(scope="session")
|
| 12 |
+
def obb(pytestconfig):
|
| 13 |
+
"""Fixture to setup obb."""
|
| 14 |
+
if pytestconfig.getoption("markexpr") != "not integration":
|
| 15 |
+
import openbb
|
| 16 |
+
|
| 17 |
+
return openbb.obb
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
@parametrize(
|
| 21 |
+
"params",
|
| 22 |
+
[
|
| 23 |
+
(
|
| 24 |
+
{
|
| 25 |
+
"provider": "intrinio",
|
| 26 |
+
"symbol": "AAPL",
|
| 27 |
+
"date": "2023-01-25",
|
| 28 |
+
"option_type": None,
|
| 29 |
+
"moneyness": "all",
|
| 30 |
+
"strike_gt": None,
|
| 31 |
+
"strike_lt": None,
|
| 32 |
+
"volume_gt": None,
|
| 33 |
+
"volume_lt": None,
|
| 34 |
+
"oi_gt": None,
|
| 35 |
+
"oi_lt": None,
|
| 36 |
+
"model": "black_scholes",
|
| 37 |
+
"show_extended_price": False,
|
| 38 |
+
"include_related_symbols": False,
|
| 39 |
+
"delay": "delayed",
|
| 40 |
+
}
|
| 41 |
+
),
|
| 42 |
+
({"provider": "cboe", "symbol": "AAPL", "use_cache": False}),
|
| 43 |
+
({"provider": "tradier", "symbol": "AAPL"}),
|
| 44 |
+
({"provider": "yfinance", "symbol": "AAPL"}),
|
| 45 |
+
({"provider": "deribit", "symbol": "BTC"}),
|
| 46 |
+
(
|
| 47 |
+
{
|
| 48 |
+
"provider": "tmx",
|
| 49 |
+
"symbol": "SHOP",
|
| 50 |
+
"date": "2022-12-28",
|
| 51 |
+
"use_cache": False,
|
| 52 |
+
}
|
| 53 |
+
),
|
| 54 |
+
],
|
| 55 |
+
)
|
| 56 |
+
@pytest.mark.integration
|
| 57 |
+
def test_derivatives_options_chains(params, obb):
|
| 58 |
+
"""Test the options chains endpoint."""
|
| 59 |
+
result = obb.derivatives.options.chains(**params)
|
| 60 |
+
assert result
|
| 61 |
+
assert isinstance(result, OBBject)
|
| 62 |
+
result = result.results # type: ignore
|
| 63 |
+
list_msg = "Unexpected data format, expected List"
|
| 64 |
+
oi_msg = "Unexpected keys in total_oi property, expected ['total', 'expiration', 'strike']"
|
| 65 |
+
assert isinstance(result.expirations, list), list_msg # type: ignore
|
| 66 |
+
assert isinstance(result.strikes, list), list_msg # type: ignore
|
| 67 |
+
assert isinstance(result.contract_symbol, list), list_msg # type: ignore
|
| 68 |
+
assert hasattr(result, "total_oi"), "Missing total_oi property" # type: ignore
|
| 69 |
+
assert isinstance(result.total_oi, dict), "Unexpected property format, expected dictionary." # type: ignore
|
| 70 |
+
assert list(result.total_oi) == ["total", "expiration", "strike"], oi_msg # type: ignore
|
| 71 |
+
assert hasattr(result, "dataframe"), "Missing dataframe attribute" # type: ignore
|
| 72 |
+
assert result.has_iv, "Expected implied volatility data" # type: ignore
|
| 73 |
+
assert len(getattr(result, "dataframe", [])) == len(result.contract_symbol) # type: ignore
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
@parametrize(
|
| 77 |
+
"params",
|
| 78 |
+
[
|
| 79 |
+
(
|
| 80 |
+
{
|
| 81 |
+
"symbol": "AAPL",
|
| 82 |
+
"provider": "intrinio",
|
| 83 |
+
"start_date": "2023-11-20",
|
| 84 |
+
"end_date": None,
|
| 85 |
+
"min_value": None,
|
| 86 |
+
"max_value": None,
|
| 87 |
+
"trade_type": None,
|
| 88 |
+
"sentiment": "neutral",
|
| 89 |
+
"limit": 1000,
|
| 90 |
+
"source": "delayed",
|
| 91 |
+
}
|
| 92 |
+
)
|
| 93 |
+
],
|
| 94 |
+
)
|
| 95 |
+
@pytest.mark.integration
|
| 96 |
+
def test_derivatives_options_unusual(params, obb):
|
| 97 |
+
"""Test the unusual options endpoint."""
|
| 98 |
+
result = obb.derivatives.options.unusual(**params)
|
| 99 |
+
assert result
|
| 100 |
+
assert isinstance(result, OBBject)
|
| 101 |
+
assert len(result.results) > 0
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
@parametrize(
|
| 105 |
+
"params",
|
| 106 |
+
[
|
| 107 |
+
(
|
| 108 |
+
{
|
| 109 |
+
"provider": "yfinance",
|
| 110 |
+
"interval": "1d",
|
| 111 |
+
"symbol": "CL,BZ",
|
| 112 |
+
"start_date": "2023-01-01",
|
| 113 |
+
"end_date": "2023-06-06",
|
| 114 |
+
"expiration": "2025-12",
|
| 115 |
+
}
|
| 116 |
+
),
|
| 117 |
+
(
|
| 118 |
+
{
|
| 119 |
+
"provider": "deribit",
|
| 120 |
+
"interval": "1d",
|
| 121 |
+
"symbol": "BTC,ETH",
|
| 122 |
+
"start_date": "2023-01-01",
|
| 123 |
+
"end_date": "2023-06-06",
|
| 124 |
+
}
|
| 125 |
+
),
|
| 126 |
+
],
|
| 127 |
+
)
|
| 128 |
+
@pytest.mark.integration
|
| 129 |
+
def test_derivatives_futures_historical(params, obb):
|
| 130 |
+
"""Test the futures historical endpoint."""
|
| 131 |
+
result = obb.derivatives.futures.historical(**params)
|
| 132 |
+
assert result
|
| 133 |
+
assert isinstance(result, OBBject)
|
| 134 |
+
assert len(result.results) > 0
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
@parametrize(
|
| 138 |
+
"params",
|
| 139 |
+
[
|
| 140 |
+
({"provider": "yfinance", "symbol": "ES", "date": None}),
|
| 141 |
+
({"provider": "cboe", "symbol": "VX", "date": "2024-06-25"}),
|
| 142 |
+
({"provider": "deribit", "date": None, "symbol": "BTC", "hours_ago": 12}),
|
| 143 |
+
],
|
| 144 |
+
)
|
| 145 |
+
@pytest.mark.integration
|
| 146 |
+
def test_derivatives_futures_curve(params, obb):
|
| 147 |
+
"""Test the futures curve endpoint."""
|
| 148 |
+
result = obb.derivatives.futures.curve(**params)
|
| 149 |
+
assert result
|
| 150 |
+
assert isinstance(result, OBBject)
|
| 151 |
+
assert len(result.results) > 0
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
@parametrize(
|
| 155 |
+
"params",
|
| 156 |
+
[
|
| 157 |
+
({"provider": "intrinio", "date": None, "only_traded": True}),
|
| 158 |
+
],
|
| 159 |
+
)
|
| 160 |
+
@pytest.mark.skip(
|
| 161 |
+
reason="This test is skipped because the download is excessively large."
|
| 162 |
+
)
|
| 163 |
+
def test_derivatives_options_snapshots(params, obb):
|
| 164 |
+
"""Test the options snapshots endpoint."""
|
| 165 |
+
result = obb.derivatives.options.snapshots(**params)
|
| 166 |
+
assert result
|
| 167 |
+
assert isinstance(result, OBBject)
|
| 168 |
+
assert len(result.results) > 0
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
@parametrize(
|
| 172 |
+
"params",
|
| 173 |
+
[
|
| 174 |
+
({"provider": "deribit"}),
|
| 175 |
+
],
|
| 176 |
+
)
|
| 177 |
+
@pytest.mark.integration
|
| 178 |
+
def test_derivatives_futures_instruments(params, obb):
|
| 179 |
+
"""Test the futures instruments endpoint."""
|
| 180 |
+
result = obb.derivatives.futures.instruments(**params)
|
| 181 |
+
assert result
|
| 182 |
+
assert isinstance(result, OBBject)
|
| 183 |
+
assert len(result.results) > 0
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
@parametrize(
|
| 187 |
+
"params",
|
| 188 |
+
[
|
| 189 |
+
({"provider": "deribit", "symbol": "ETH-PERPETUAL"}),
|
| 190 |
+
],
|
| 191 |
+
)
|
| 192 |
+
@pytest.mark.integration
|
| 193 |
+
def test_derivatives_futures_info(params, obb):
|
| 194 |
+
"""Test the futures info endpoint."""
|
| 195 |
+
result = obb.derivatives.futures.info(**params)
|
| 196 |
+
assert result
|
| 197 |
+
assert isinstance(result, OBBject)
|
| 198 |
+
assert len(result.results) > 0
|
openbb_platform/extensions/derivatives/openbb_derivatives/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""Options."""
|
openbb_platform/extensions/derivatives/openbb_derivatives/derivatives_router.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Derivatives Router."""
|
| 2 |
+
|
| 3 |
+
from openbb_core.app.router import Router
|
| 4 |
+
|
| 5 |
+
from openbb_derivatives.futures.futures_router import router as futures_router
|
| 6 |
+
from openbb_derivatives.options.options_router import router as options_router
|
| 7 |
+
|
| 8 |
+
router = Router(prefix="", description="Derivatives market data.")
|
| 9 |
+
router.include_router(options_router)
|
| 10 |
+
router.include_router(futures_router)
|
openbb_platform/extensions/derivatives/openbb_derivatives/derivatives_views.py
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Views for the Derivatives Extension."""
|
| 2 |
+
|
| 3 |
+
from typing import TYPE_CHECKING, Any, Dict, Tuple
|
| 4 |
+
|
| 5 |
+
if TYPE_CHECKING:
|
| 6 |
+
from openbb_charting.core.openbb_figure import OpenBBFigure
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class DerivativesViews:
|
| 10 |
+
"""Derivatives Views."""
|
| 11 |
+
|
| 12 |
+
@staticmethod
|
| 13 |
+
def derivatives_futures_historical( # noqa: PLR0912
|
| 14 |
+
**kwargs,
|
| 15 |
+
) -> Tuple["OpenBBFigure", Dict[str, Any]]:
|
| 16 |
+
"""Get Derivatives Price Historical Chart."""
|
| 17 |
+
# pylint: disable=import-outside-toplevel
|
| 18 |
+
from openbb_charting.charts.price_historical import price_historical
|
| 19 |
+
|
| 20 |
+
kwargs.update({"candles": False, "same_axis": False})
|
| 21 |
+
|
| 22 |
+
return price_historical(**kwargs)
|
| 23 |
+
|
| 24 |
+
@staticmethod
|
| 25 |
+
def derivatives_futures_curve( # noqa: PLR0912
|
| 26 |
+
**kwargs,
|
| 27 |
+
) -> Tuple["OpenBBFigure", Dict[str, Any]]:
|
| 28 |
+
"""Futures curve chart. All parameters are optional, and are kwargs.
|
| 29 |
+
Parameters can be directly accessed from the function end point by
|
| 30 |
+
entering as a nested dictionary to the 'chart_params' key.
|
| 31 |
+
|
| 32 |
+
From the API, `chart_params` must be passed as a JSON in the request body with `extra_params`.
|
| 33 |
+
|
| 34 |
+
If using the chart post-request, the parameters are passed directly
|
| 35 |
+
as `key=value` pairs in the `charting.to_chart` or `charting.show` methods.
|
| 36 |
+
|
| 37 |
+
Parameters
|
| 38 |
+
----------
|
| 39 |
+
data : Optional[Union[List[Data], DataFrame]]
|
| 40 |
+
Data for the chart. Required fields are: 'expiration' and 'price'.
|
| 41 |
+
Multiple dates will be plotted on the same chart.
|
| 42 |
+
If not supplied, the original OBBject.results will be used.
|
| 43 |
+
If a DataFrame is supplied, flat data is expected, without a set index.
|
| 44 |
+
title: Optional[str]
|
| 45 |
+
Title for the chart. If not supplied, a default title will be used.
|
| 46 |
+
colors: Optional[List[str]]
|
| 47 |
+
List of colors to use for the chart. If not supplied, the default colorway will be used.
|
| 48 |
+
Colors should be in hex format, or named Plotly colors. Invalid colors will raise a Plotly error.
|
| 49 |
+
layout_kwargs: Optional[Dict[str, Any]]
|
| 50 |
+
Additional layout parameters for the chart, passed directly to `figure.update_layout` before output.
|
| 51 |
+
See Plotly documentation for available options.
|
| 52 |
+
|
| 53 |
+
Returns
|
| 54 |
+
-------
|
| 55 |
+
Tuple[OpenBBFigure, Dict[str, Any]]
|
| 56 |
+
Tuple with the OpenBBFigure object, and the JSON-serialized content.
|
| 57 |
+
If using the API, only the JSON content will be returned.
|
| 58 |
+
|
| 59 |
+
Examples
|
| 60 |
+
--------
|
| 61 |
+
```python
|
| 62 |
+
from openbb import obb
|
| 63 |
+
data = obb.derivatives.futures.curve(symbol="vx", provider="cboe", date=["2020-03-31", "2024-06-28"], chart=True)
|
| 64 |
+
data.show()
|
| 65 |
+
```
|
| 66 |
+
|
| 67 |
+
Redraw the chart, from the same data, with a custom colorway and title:
|
| 68 |
+
|
| 69 |
+
```python
|
| 70 |
+
data.charting.to_chart(colors=["green", "red"], title="VIX Futures Curve - 2020 vs. 2024")
|
| 71 |
+
```
|
| 72 |
+
"""
|
| 73 |
+
# pylint: disable=import-outside-toplevel
|
| 74 |
+
from openbb_charting.core.chart_style import ChartStyle
|
| 75 |
+
from openbb_charting.core.openbb_figure import OpenBBFigure
|
| 76 |
+
from openbb_charting.styles.colors import LARGE_CYCLER
|
| 77 |
+
from openbb_core.app.model.abstract.error import OpenBBError
|
| 78 |
+
from openbb_core.provider.abstract.data import Data
|
| 79 |
+
from pandas import DataFrame, to_datetime
|
| 80 |
+
|
| 81 |
+
data = kwargs.get("data")
|
| 82 |
+
symbol = kwargs.get("standard_params", {}).get("symbol", "")
|
| 83 |
+
df: DataFrame = DataFrame()
|
| 84 |
+
if data:
|
| 85 |
+
if isinstance(data, DataFrame) and not data.empty: # noqa: SIM108
|
| 86 |
+
df = data
|
| 87 |
+
elif isinstance(data, (list, Data)):
|
| 88 |
+
df = DataFrame([d.model_dump(exclude_none=True, exclude_unset=True) for d in data]) # type: ignore
|
| 89 |
+
else:
|
| 90 |
+
pass
|
| 91 |
+
else:
|
| 92 |
+
df = DataFrame(
|
| 93 |
+
[
|
| 94 |
+
d.model_dump(exclude_none=True, exclude_unset=True) # type: ignore
|
| 95 |
+
for d in kwargs["obbject_item"]
|
| 96 |
+
]
|
| 97 |
+
if isinstance(kwargs.get("obbject_item"), list)
|
| 98 |
+
else kwargs["obbject_item"].model_dump(exclude_none=True, exclude_unset=True) # type: ignore
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
if df.empty:
|
| 102 |
+
raise OpenBBError("Error: No data to plot.")
|
| 103 |
+
|
| 104 |
+
if "expiration" not in df.columns:
|
| 105 |
+
raise OpenBBError("Expiration field not found in the data.")
|
| 106 |
+
|
| 107 |
+
if "price" not in df.columns:
|
| 108 |
+
raise ValueError("Price field not found in the data.")
|
| 109 |
+
|
| 110 |
+
provider = kwargs.get("provider", "")
|
| 111 |
+
|
| 112 |
+
if provider != "deribit":
|
| 113 |
+
df["expiration"] = df["expiration"].apply(to_datetime).dt.strftime("%b-%Y")
|
| 114 |
+
|
| 115 |
+
if (
|
| 116 |
+
provider == "cboe"
|
| 117 |
+
and "date" in df.columns
|
| 118 |
+
and len(df["date"].unique()) > 1
|
| 119 |
+
and "symbol" in df.columns
|
| 120 |
+
):
|
| 121 |
+
df["expiration"] = df.symbol
|
| 122 |
+
|
| 123 |
+
# Use a complete list of expirations to categorize the x-axis across all dates.
|
| 124 |
+
expirations = df["expiration"].unique().tolist()
|
| 125 |
+
|
| 126 |
+
# Use the supplied colors, if any.
|
| 127 |
+
colors = kwargs.get("colors", [])
|
| 128 |
+
if not colors:
|
| 129 |
+
colors = LARGE_CYCLER
|
| 130 |
+
color_count = 0
|
| 131 |
+
|
| 132 |
+
figure = OpenBBFigure().create_subplots(shared_xaxes=True)
|
| 133 |
+
figure.update_layout(ChartStyle().plotly_template.get("layout", {}))
|
| 134 |
+
text_color = "white" if ChartStyle().plt_style == "dark" else "black"
|
| 135 |
+
|
| 136 |
+
def create_fig(figure, df, dates, color_count):
|
| 137 |
+
"""Create a scatter for each date in the data."""
|
| 138 |
+
for date in dates:
|
| 139 |
+
color = colors[color_count % len(colors)]
|
| 140 |
+
plot_df = (
|
| 141 |
+
df[df["date"].astype(str) == date].copy()
|
| 142 |
+
if "date" in df.columns
|
| 143 |
+
else df.copy()
|
| 144 |
+
)
|
| 145 |
+
plot_df = plot_df.drop(
|
| 146 |
+
columns=["date"] if "date" in plot_df.columns else []
|
| 147 |
+
).rename(columns={"expiration": "Expiration", "price": "Price"})
|
| 148 |
+
figure.add_scatter(
|
| 149 |
+
x=plot_df["Expiration"],
|
| 150 |
+
y=plot_df["Price"],
|
| 151 |
+
mode="lines+markers",
|
| 152 |
+
name=date,
|
| 153 |
+
line=dict(width=3, color=color),
|
| 154 |
+
marker=dict(size=10, color=color),
|
| 155 |
+
hovertemplate=(
|
| 156 |
+
"Expiration: %{x}<br>Price: $%{y}<extra></extra>"
|
| 157 |
+
if len(dates) == 1
|
| 158 |
+
else "%{fullData.name}<br>Expiration: %{x}<br>Price: $%{y}<extra></extra>"
|
| 159 |
+
),
|
| 160 |
+
)
|
| 161 |
+
color_count += 1
|
| 162 |
+
return figure, color_count
|
| 163 |
+
|
| 164 |
+
dates = (
|
| 165 |
+
df.date.astype(str).unique().tolist()
|
| 166 |
+
if "date" in df.columns
|
| 167 |
+
else ["Current"]
|
| 168 |
+
)
|
| 169 |
+
|
| 170 |
+
if provider == "deribit" and "hours_ago" in df.columns:
|
| 171 |
+
dates = [
|
| 172 |
+
str(d) + " Hours Ago" if d > 0 else "Current"
|
| 173 |
+
for d in df["hours_ago"].unique().tolist()
|
| 174 |
+
]
|
| 175 |
+
df.loc[:, "date"] = df["hours_ago"].apply(
|
| 176 |
+
lambda x: str(x) + " Hours Ago" if x > 0 else "Current"
|
| 177 |
+
)
|
| 178 |
+
figure, color_count = create_fig(figure, df, dates, color_count)
|
| 179 |
+
|
| 180 |
+
# Set the title for the chart
|
| 181 |
+
title: str = ""
|
| 182 |
+
if provider == "cboe":
|
| 183 |
+
vx_eod_symbols = ["vx", "vix", "vx_eod", "^vix"]
|
| 184 |
+
title = (
|
| 185 |
+
"VIX EOD Futures Curve"
|
| 186 |
+
if symbol.lower() in vx_eod_symbols
|
| 187 |
+
else "VIX Mid-Morning TWAP Futures Curve"
|
| 188 |
+
)
|
| 189 |
+
if len(dates) == 1 and dates[0] != "Current":
|
| 190 |
+
title = f"{title} for {dates[0]}"
|
| 191 |
+
else:
|
| 192 |
+
title = f"{symbol.upper()} Futures Curve"
|
| 193 |
+
|
| 194 |
+
# Use the supplied title, if any.
|
| 195 |
+
title = kwargs.get("title", title)
|
| 196 |
+
|
| 197 |
+
# Update the layout of the figure.
|
| 198 |
+
figure.update_layout(
|
| 199 |
+
title=dict(text=title, x=0.5, font=dict(size=20)),
|
| 200 |
+
plot_bgcolor=(
|
| 201 |
+
"rgba(0,0,0,0)" if text_color == "white" else "rgba(255,255,255,0)"
|
| 202 |
+
),
|
| 203 |
+
paper_bgcolor=(
|
| 204 |
+
"rgba(0,0,0,0)" if text_color == "white" else "rgba(255,255,255,0)"
|
| 205 |
+
),
|
| 206 |
+
xaxis=dict(
|
| 207 |
+
title="",
|
| 208 |
+
ticklen=0,
|
| 209 |
+
showgrid=False,
|
| 210 |
+
type="category",
|
| 211 |
+
categoryorder="array",
|
| 212 |
+
categoryarray=expirations,
|
| 213 |
+
),
|
| 214 |
+
yaxis=dict(
|
| 215 |
+
title="Price ($)",
|
| 216 |
+
ticklen=0,
|
| 217 |
+
showgrid=True,
|
| 218 |
+
gridcolor="rgba(128,128,128,0.3)",
|
| 219 |
+
),
|
| 220 |
+
legend=dict(
|
| 221 |
+
orientation="v",
|
| 222 |
+
yanchor="top",
|
| 223 |
+
xanchor="right",
|
| 224 |
+
y=0.95,
|
| 225 |
+
x=0,
|
| 226 |
+
xref="paper",
|
| 227 |
+
font=dict(size=12),
|
| 228 |
+
bgcolor=(
|
| 229 |
+
"rgba(0,0,0,0)" if text_color == "white" else "rgba(255,255,255,0)"
|
| 230 |
+
),
|
| 231 |
+
),
|
| 232 |
+
margin=dict(
|
| 233 |
+
b=10,
|
| 234 |
+
t=10,
|
| 235 |
+
),
|
| 236 |
+
)
|
| 237 |
+
|
| 238 |
+
layout_kwargs = kwargs.get("layout_kwargs", {})
|
| 239 |
+
if layout_kwargs:
|
| 240 |
+
figure.update_layout(layout_kwargs)
|
| 241 |
+
|
| 242 |
+
content = figure.show(external=True).to_plotly_json()
|
| 243 |
+
|
| 244 |
+
return figure, content
|
openbb_platform/extensions/derivatives/openbb_derivatives/futures/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""Futures."""
|
openbb_platform/extensions/derivatives/openbb_derivatives/futures/futures_router.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Futures Router."""
|
| 2 |
+
|
| 3 |
+
from openbb_core.app.model.command_context import CommandContext
|
| 4 |
+
from openbb_core.app.model.example import APIEx
|
| 5 |
+
from openbb_core.app.model.obbject import OBBject
|
| 6 |
+
from openbb_core.app.provider_interface import (
|
| 7 |
+
ExtraParams,
|
| 8 |
+
ProviderChoices,
|
| 9 |
+
StandardParams,
|
| 10 |
+
)
|
| 11 |
+
from openbb_core.app.query import Query
|
| 12 |
+
from openbb_core.app.router import Router
|
| 13 |
+
|
| 14 |
+
router = Router(prefix="/futures")
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
# pylint: disable=unused-argument
|
| 18 |
+
@router.command(
|
| 19 |
+
model="FuturesHistorical",
|
| 20 |
+
examples=[
|
| 21 |
+
APIEx(parameters={"symbol": "ES", "provider": "yfinance"}),
|
| 22 |
+
APIEx(
|
| 23 |
+
description="Enter multiple symbols.",
|
| 24 |
+
parameters={"symbol": "ES,NQ", "provider": "yfinance"},
|
| 25 |
+
),
|
| 26 |
+
APIEx(
|
| 27 |
+
description='Enter expiration dates as "YYYY-MM".',
|
| 28 |
+
parameters={
|
| 29 |
+
"symbol": "ES",
|
| 30 |
+
"provider": "yfinance",
|
| 31 |
+
"expiration": "2025-12",
|
| 32 |
+
},
|
| 33 |
+
),
|
| 34 |
+
],
|
| 35 |
+
)
|
| 36 |
+
async def historical(
|
| 37 |
+
cc: CommandContext,
|
| 38 |
+
provider_choices: ProviderChoices,
|
| 39 |
+
standard_params: StandardParams,
|
| 40 |
+
extra_params: ExtraParams,
|
| 41 |
+
) -> OBBject:
|
| 42 |
+
"""Historical futures prices."""
|
| 43 |
+
return await OBBject.from_query(Query(**locals()))
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
@router.command(
|
| 47 |
+
model="FuturesCurve",
|
| 48 |
+
examples=[
|
| 49 |
+
APIEx(parameters={"symbol": "VX", "provider": "cboe", "date": "2024-06-25"}),
|
| 50 |
+
APIEx(
|
| 51 |
+
parameters={"symbol": "NG", "provider": "yfinance"},
|
| 52 |
+
),
|
| 53 |
+
],
|
| 54 |
+
)
|
| 55 |
+
async def curve(
|
| 56 |
+
cc: CommandContext,
|
| 57 |
+
provider_choices: ProviderChoices,
|
| 58 |
+
standard_params: StandardParams,
|
| 59 |
+
extra_params: ExtraParams,
|
| 60 |
+
) -> OBBject:
|
| 61 |
+
"""Futures Term Structure, current or historical."""
|
| 62 |
+
return await OBBject.from_query(Query(**locals()))
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
@router.command(
|
| 66 |
+
model="FuturesInstruments",
|
| 67 |
+
examples=[
|
| 68 |
+
APIEx(parameters={"provider": "deribit"}),
|
| 69 |
+
],
|
| 70 |
+
)
|
| 71 |
+
async def instruments(
|
| 72 |
+
cc: CommandContext,
|
| 73 |
+
provider_choices: ProviderChoices,
|
| 74 |
+
standard_params: StandardParams,
|
| 75 |
+
extra_params: ExtraParams,
|
| 76 |
+
) -> OBBject:
|
| 77 |
+
"""Get reference data for available futures instruments by provider."""
|
| 78 |
+
return await OBBject.from_query(Query(**locals()))
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
@router.command(
|
| 82 |
+
model="FuturesInfo",
|
| 83 |
+
examples=[
|
| 84 |
+
APIEx(parameters={"provider": "deribit", "symbol": "BTC"}),
|
| 85 |
+
APIEx(parameters={"provider": "deribit", "symbol": "SOLUSDC"}),
|
| 86 |
+
APIEx(parameters={"provider": "deribit", "symbol": "SOL_USDC-PERPETUAL"}),
|
| 87 |
+
APIEx(parameters={"provider": "deribit", "symbol": "BTC,ETH"}),
|
| 88 |
+
],
|
| 89 |
+
)
|
| 90 |
+
async def info(
|
| 91 |
+
cc: CommandContext,
|
| 92 |
+
provider_choices: ProviderChoices,
|
| 93 |
+
standard_params: StandardParams,
|
| 94 |
+
extra_params: ExtraParams,
|
| 95 |
+
) -> OBBject:
|
| 96 |
+
"""Get current trading statistics by futures contract symbol."""
|
| 97 |
+
return await OBBject.from_query(Query(**locals()))
|
openbb_platform/extensions/derivatives/openbb_derivatives/options/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""Options."""
|
openbb_platform/extensions/derivatives/openbb_derivatives/options/options_router.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Options Router."""
|
| 2 |
+
|
| 3 |
+
from openbb_core.app.model.command_context import CommandContext
|
| 4 |
+
from openbb_core.app.model.example import APIEx
|
| 5 |
+
from openbb_core.app.model.obbject import OBBject
|
| 6 |
+
from openbb_core.app.provider_interface import (
|
| 7 |
+
ExtraParams,
|
| 8 |
+
ProviderChoices,
|
| 9 |
+
StandardParams,
|
| 10 |
+
)
|
| 11 |
+
from openbb_core.app.query import Query
|
| 12 |
+
from openbb_core.app.router import Router
|
| 13 |
+
|
| 14 |
+
router = Router(prefix="/options")
|
| 15 |
+
|
| 16 |
+
# pylint: disable=unused-argument
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
@router.command(
|
| 20 |
+
model="OptionsChains",
|
| 21 |
+
examples=[
|
| 22 |
+
APIEx(parameters={"symbol": "AAPL", "provider": "intrinio"}),
|
| 23 |
+
APIEx(
|
| 24 |
+
description='Use the "date" parameter to get the end-of-day-data for a specific date, where supported.',
|
| 25 |
+
parameters={"symbol": "AAPL", "date": "2023-01-25", "provider": "intrinio"},
|
| 26 |
+
),
|
| 27 |
+
],
|
| 28 |
+
)
|
| 29 |
+
async def chains(
|
| 30 |
+
cc: CommandContext,
|
| 31 |
+
provider_choices: ProviderChoices,
|
| 32 |
+
standard_params: StandardParams,
|
| 33 |
+
extra_params: ExtraParams,
|
| 34 |
+
) -> OBBject:
|
| 35 |
+
"""Get the complete options chain for a ticker."""
|
| 36 |
+
return await OBBject.from_query(Query(**locals()))
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
@router.command(
|
| 40 |
+
model="OptionsUnusual",
|
| 41 |
+
examples=[
|
| 42 |
+
APIEx(parameters={"symbol": "TSLA", "provider": "intrinio"}),
|
| 43 |
+
APIEx(
|
| 44 |
+
description="Use the 'symbol' parameter to get the most recent activity for a specific symbol.",
|
| 45 |
+
parameters={"symbol": "TSLA", "provider": "intrinio"},
|
| 46 |
+
),
|
| 47 |
+
],
|
| 48 |
+
)
|
| 49 |
+
async def unusual(
|
| 50 |
+
cc: CommandContext,
|
| 51 |
+
provider_choices: ProviderChoices,
|
| 52 |
+
standard_params: StandardParams,
|
| 53 |
+
extra_params: ExtraParams,
|
| 54 |
+
) -> OBBject:
|
| 55 |
+
"""Get the complete options chain for a ticker."""
|
| 56 |
+
return await OBBject.from_query(Query(**locals()))
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
@router.command(
|
| 60 |
+
model="OptionsSnapshots",
|
| 61 |
+
examples=[
|
| 62 |
+
APIEx(
|
| 63 |
+
parameters={"provider": "intrinio"},
|
| 64 |
+
),
|
| 65 |
+
],
|
| 66 |
+
)
|
| 67 |
+
async def snapshots(
|
| 68 |
+
cc: CommandContext,
|
| 69 |
+
provider_choices: ProviderChoices,
|
| 70 |
+
standard_params: StandardParams,
|
| 71 |
+
extra_params: ExtraParams,
|
| 72 |
+
) -> OBBject:
|
| 73 |
+
"""Get a snapshot of the options market universe."""
|
| 74 |
+
return await OBBject.from_query(Query(**locals()))
|
openbb_platform/extensions/derivatives/poetry.lock
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
openbb_platform/extensions/derivatives/pyproject.toml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[tool.poetry]
|
| 2 |
+
name = "openbb-derivatives"
|
| 3 |
+
version = "1.4.1"
|
| 4 |
+
description = "Derivatives extension for OpenBB"
|
| 5 |
+
authors = ["OpenBB Team <hello@openbb.co>"]
|
| 6 |
+
license = "AGPL-3.0-only"
|
| 7 |
+
readme = "README.md"
|
| 8 |
+
packages = [{ include = "openbb_derivatives" }]
|
| 9 |
+
|
| 10 |
+
[tool.poetry.dependencies]
|
| 11 |
+
python = ">=3.9.21,<3.13"
|
| 12 |
+
openbb-core = "^1.4.6"
|
| 13 |
+
|
| 14 |
+
[build-system]
|
| 15 |
+
requires = ["poetry-core"]
|
| 16 |
+
build-backend = "poetry.core.masonry.api"
|
| 17 |
+
|
| 18 |
+
[tool.poetry.plugins."openbb_core_extension"]
|
| 19 |
+
derivatives = "openbb_derivatives.derivatives_router:router"
|
| 20 |
+
|
| 21 |
+
[tool.poetry.plugins."openbb_charting_extension"]
|
| 22 |
+
derivatives = "openbb_derivatives.derivatives_views:DerivativesViews"
|
openbb_platform/extensions/derivatives/tests/.gitkeep
ADDED
|
File without changes
|
openbb_platform/extensions/devtools/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# The OpenBB DevTools Extension
|
| 2 |
+
|
| 3 |
+
This extension aggregates the dependencies that facilitate a nice development experience
|
| 4 |
+
for OpenBB. It does not contain any code itself, but rather pulls in the following dependencies:
|
| 5 |
+
|
| 6 |
+
- Linters (ruff, pylint, mypy)
|
| 7 |
+
- Code formatters (black)
|
| 8 |
+
- Code quality tools (bandit)
|
| 9 |
+
- Pre-commit hooks (pre-commit)
|
| 10 |
+
- CI/CD configuration (tox, pytest, pytest-cov)
|
| 11 |
+
- Jupyter kernel (ipykernel)
|
| 12 |
+
- ... add your productivity booster here ...
|
| 13 |
+
|
| 14 |
+
## Installation
|
| 15 |
+
|
| 16 |
+
The extension is included into the dev_install.py script.
|
| 17 |
+
|
| 18 |
+
Standalone installation:
|
| 19 |
+
|
| 20 |
+
```bash
|
| 21 |
+
pip install openbb-devtools
|
| 22 |
+
```
|
openbb_platform/extensions/devtools/integration/.gitkeep
ADDED
|
File without changes
|