openbb-aurastream 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openbb_aurastream/__init__.py +31 -0
- openbb_aurastream/aurastream_router.py +117 -0
- openbb_aurastream/models/__init__.py +1 -0
- openbb_aurastream/models/fails_to_deliver.py +99 -0
- openbb_aurastream/models/fred_vintages.py +112 -0
- openbb_aurastream/models/funding_rates.py +90 -0
- openbb_aurastream/models/net_short.py +124 -0
- openbb_aurastream/models/wasde.py +104 -0
- openbb_aurastream/utils/__init__.py +1 -0
- openbb_aurastream/utils/client.py +148 -0
- openbb_aurastream-0.1.0.dist-info/METADATA +108 -0
- openbb_aurastream-0.1.0.dist-info/RECORD +15 -0
- openbb_aurastream-0.1.0.dist-info/WHEEL +4 -0
- openbb_aurastream-0.1.0.dist-info/entry_points.txt +5 -0
- openbb_aurastream-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""OpenBB AuraStream Provider Module."""
|
|
2
|
+
|
|
3
|
+
from openbb_core.provider.abstract.provider import Provider
|
|
4
|
+
|
|
5
|
+
from openbb_aurastream.models.fails_to_deliver import AuraStreamFailsToDeliverFetcher
|
|
6
|
+
from openbb_aurastream.models.fred_vintages import AuraStreamFredVintagesFetcher
|
|
7
|
+
from openbb_aurastream.models.funding_rates import AuraStreamFundingRatesFetcher
|
|
8
|
+
from openbb_aurastream.models.net_short import AuraStreamNetShortFetcher
|
|
9
|
+
from openbb_aurastream.models.wasde import AuraStreamWasdeFetcher
|
|
10
|
+
|
|
11
|
+
aurastream_provider = Provider(
|
|
12
|
+
name="aurastream",
|
|
13
|
+
website="https://aurastream.unbiased-alpha.com",
|
|
14
|
+
description="AuraStream, a unified market data API for quants by Unbiased Alpha:"
|
|
15
|
+
" EU net short positions, USDA WASDE projections, FRED with every revision,"
|
|
16
|
+
" SEC fails-to-deliver and Binance funding rates.",
|
|
17
|
+
credentials=["api_key", "fred_api_key", "sec_user_agent"],
|
|
18
|
+
fetcher_dict={
|
|
19
|
+
"AuraStreamNetShort": AuraStreamNetShortFetcher,
|
|
20
|
+
"AuraStreamWasde": AuraStreamWasdeFetcher,
|
|
21
|
+
"AuraStreamFredVintages": AuraStreamFredVintagesFetcher,
|
|
22
|
+
"AuraStreamFailsToDeliver": AuraStreamFailsToDeliverFetcher,
|
|
23
|
+
"AuraStreamFundingRates": AuraStreamFundingRatesFetcher,
|
|
24
|
+
},
|
|
25
|
+
repr_name="AuraStream",
|
|
26
|
+
instructions="Get a free API key (500 calls a month, no credit card) at"
|
|
27
|
+
" https://aurastream.unbiased-alpha.com/sign-up and set it as aurastream_api_key."
|
|
28
|
+
" fred_vintages also needs your own free FRED key (aurastream_fred_api_key), and"
|
|
29
|
+
" fails_to_deliver a contact string for the SEC (aurastream_sec_user_agent),"
|
|
30
|
+
" e.g. 'Jane Doe jane@example.com'.",
|
|
31
|
+
)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""AuraStream 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="", description="AuraStream market data.")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@router.command(
|
|
18
|
+
model="AuraStreamNetShort",
|
|
19
|
+
examples=[
|
|
20
|
+
APIEx(
|
|
21
|
+
description="Disclosed short positions in Ubisoft on the French register.",
|
|
22
|
+
parameters={"symbol": "ubisoft", "country": "FR", "provider": "aurastream"},
|
|
23
|
+
),
|
|
24
|
+
APIEx(
|
|
25
|
+
description="Every position one fund has disclosed in the period.",
|
|
26
|
+
parameters={"symbol": "all", "holder": "citadel", "provider": "aurastream"},
|
|
27
|
+
),
|
|
28
|
+
],
|
|
29
|
+
)
|
|
30
|
+
async def net_short(
|
|
31
|
+
cc: CommandContext,
|
|
32
|
+
provider_choices: ProviderChoices,
|
|
33
|
+
standard_params: StandardParams,
|
|
34
|
+
extra_params: ExtraParams,
|
|
35
|
+
) -> OBBject:
|
|
36
|
+
"""Get disclosed net short positions of 0.5% or more in European shares (AMF, AFM, CNMV)."""
|
|
37
|
+
return await OBBject.from_query(Query(**locals()))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@router.command(
|
|
41
|
+
model="AuraStreamWasde",
|
|
42
|
+
examples=[
|
|
43
|
+
APIEx(
|
|
44
|
+
description="How the USDA's projection of US corn ending stocks changed, report by report.",
|
|
45
|
+
parameters={"symbol": "corn:ending_stocks", "provider": "aurastream"},
|
|
46
|
+
),
|
|
47
|
+
APIEx(
|
|
48
|
+
description="Brazil's soybean crop from the world tables.",
|
|
49
|
+
parameters={"symbol": "soybeans:production:Brazil", "provider": "aurastream"},
|
|
50
|
+
),
|
|
51
|
+
],
|
|
52
|
+
)
|
|
53
|
+
async def wasde(
|
|
54
|
+
cc: CommandContext,
|
|
55
|
+
provider_choices: ProviderChoices,
|
|
56
|
+
standard_params: StandardParams,
|
|
57
|
+
extra_params: ExtraParams,
|
|
58
|
+
) -> OBBject:
|
|
59
|
+
"""Get USDA WASDE projections since July 2010, one row per monthly report."""
|
|
60
|
+
return await OBBject.from_query(Query(**locals()))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@router.command(
|
|
64
|
+
model="AuraStreamFredVintages",
|
|
65
|
+
examples=[
|
|
66
|
+
APIEx(
|
|
67
|
+
description="Every published version of US CPI, by publication date.",
|
|
68
|
+
parameters={"symbol": "CPIAUCSL", "provider": "aurastream"},
|
|
69
|
+
),
|
|
70
|
+
],
|
|
71
|
+
)
|
|
72
|
+
async def fred_vintages(
|
|
73
|
+
cc: CommandContext,
|
|
74
|
+
provider_choices: ProviderChoices,
|
|
75
|
+
standard_params: StandardParams,
|
|
76
|
+
extra_params: ExtraParams,
|
|
77
|
+
) -> OBBject:
|
|
78
|
+
"""Get FRED series with every revision, stamped at the date each value was published."""
|
|
79
|
+
return await OBBject.from_query(Query(**locals()))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@router.command(
|
|
83
|
+
model="AuraStreamFailsToDeliver",
|
|
84
|
+
examples=[
|
|
85
|
+
APIEx(
|
|
86
|
+
description="Fails-to-deliver in GameStop.",
|
|
87
|
+
parameters={"symbol": "GME", "provider": "aurastream"},
|
|
88
|
+
),
|
|
89
|
+
],
|
|
90
|
+
)
|
|
91
|
+
async def fails_to_deliver(
|
|
92
|
+
cc: CommandContext,
|
|
93
|
+
provider_choices: ProviderChoices,
|
|
94
|
+
standard_params: StandardParams,
|
|
95
|
+
extra_params: ExtraParams,
|
|
96
|
+
) -> OBBject:
|
|
97
|
+
"""Get SEC fails-to-deliver for a US stock or ETF, one row per settlement date."""
|
|
98
|
+
return await OBBject.from_query(Query(**locals()))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@router.command(
|
|
102
|
+
model="AuraStreamFundingRates",
|
|
103
|
+
examples=[
|
|
104
|
+
APIEx(
|
|
105
|
+
description="Funding rates of the Bitcoin and Ether perpetuals.",
|
|
106
|
+
parameters={"symbol": "BTCUSDT,ETHUSDT", "provider": "aurastream"},
|
|
107
|
+
),
|
|
108
|
+
],
|
|
109
|
+
)
|
|
110
|
+
async def funding_rates(
|
|
111
|
+
cc: CommandContext,
|
|
112
|
+
provider_choices: ProviderChoices,
|
|
113
|
+
standard_params: StandardParams,
|
|
114
|
+
extra_params: ExtraParams,
|
|
115
|
+
) -> OBBject:
|
|
116
|
+
"""Get Binance USD-M perpetual funding rates at each funding time."""
|
|
117
|
+
return await OBBject.from_query(Query(**locals()))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""AuraStream models."""
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""AuraStream Fails-to-Deliver Model."""
|
|
2
|
+
|
|
3
|
+
from datetime import date as dateType, datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from openbb_core.provider.abstract.data import Data
|
|
7
|
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
|
8
|
+
from openbb_core.provider.standard_models.bls_series import SeriesQueryParams
|
|
9
|
+
from pydantic import Field
|
|
10
|
+
|
|
11
|
+
from openbb_aurastream.utils.client import (
|
|
12
|
+
api_key,
|
|
13
|
+
default_start,
|
|
14
|
+
fetch,
|
|
15
|
+
require_key,
|
|
16
|
+
split_symbols,
|
|
17
|
+
to_rows,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AuraStreamFailsToDeliverQueryParams(SeriesQueryParams):
|
|
22
|
+
"""AuraStream Fails-to-Deliver Query.
|
|
23
|
+
|
|
24
|
+
Source: https://aurastream.unbiased-alpha.com/docs/data-sources/edgar/
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
|
|
28
|
+
|
|
29
|
+
symbol: str = Field(
|
|
30
|
+
description="US stock or ETF symbol (e.g. 'GME') or 9-character CUSIP."
|
|
31
|
+
)
|
|
32
|
+
start_date: dateType | None = Field(
|
|
33
|
+
default=None, description="Start date. Default: one year ago."
|
|
34
|
+
)
|
|
35
|
+
end_date: dateType | None = Field(
|
|
36
|
+
default=None, description="End date. Default: today."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class AuraStreamFailsToDeliverData(Data):
|
|
41
|
+
"""AuraStream Fails-to-Deliver Data. One row per settlement date with fails outstanding."""
|
|
42
|
+
|
|
43
|
+
date: datetime = Field(description="Settlement date.")
|
|
44
|
+
symbol: str = Field(description="Security symbol.")
|
|
45
|
+
fails: int | None = Field(
|
|
46
|
+
default=None, description="Total fails-to-deliver balance, in shares."
|
|
47
|
+
)
|
|
48
|
+
price: float | None = Field(
|
|
49
|
+
default=None, description="Previous day's closing price."
|
|
50
|
+
)
|
|
51
|
+
cusip: str | None = Field(default=None, description="CUSIP of the security.")
|
|
52
|
+
description: str | None = Field(default=None, description="Security description.")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class AuraStreamFailsToDeliverFetcher(
|
|
56
|
+
Fetcher[AuraStreamFailsToDeliverQueryParams, list[AuraStreamFailsToDeliverData]]
|
|
57
|
+
):
|
|
58
|
+
"""Fetch SEC fails-to-deliver from AuraStream."""
|
|
59
|
+
|
|
60
|
+
require_credentials = False
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def transform_query(params: dict[str, Any]) -> AuraStreamFailsToDeliverQueryParams:
|
|
64
|
+
"""Transform the query."""
|
|
65
|
+
return AuraStreamFailsToDeliverQueryParams(**params)
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
async def aextract_data(
|
|
69
|
+
query: AuraStreamFailsToDeliverQueryParams,
|
|
70
|
+
credentials: dict[str, str] | None,
|
|
71
|
+
**kwargs: Any,
|
|
72
|
+
) -> list[dict]:
|
|
73
|
+
"""Return the raw rows."""
|
|
74
|
+
key = api_key(credentials)
|
|
75
|
+
user_agent = require_key(
|
|
76
|
+
credentials,
|
|
77
|
+
"aurastream_sec_user_agent",
|
|
78
|
+
"The SEC asks every automated request to name the sender: your name or company"
|
|
79
|
+
" and a contact email, e.g. 'Jane Doe jane@example.com'.",
|
|
80
|
+
)
|
|
81
|
+
symbols = [s.upper() for s in split_symbols(query.symbol)]
|
|
82
|
+
body = {
|
|
83
|
+
"tickerlist": symbols,
|
|
84
|
+
"start": str(query.start_date or default_start()),
|
|
85
|
+
"end": str(query.end_date or dateType.today()),
|
|
86
|
+
"interval": "1d",
|
|
87
|
+
"source": "edgar",
|
|
88
|
+
"dataset": "ftd",
|
|
89
|
+
"credentials": {"user_agent": user_agent},
|
|
90
|
+
}
|
|
91
|
+
data = await fetch(body, key)
|
|
92
|
+
return to_rows(data, symbols)
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def transform_data(
|
|
96
|
+
query: AuraStreamFailsToDeliverQueryParams, data: list[dict], **kwargs: Any
|
|
97
|
+
) -> list[AuraStreamFailsToDeliverData]:
|
|
98
|
+
"""Transform the data."""
|
|
99
|
+
return [AuraStreamFailsToDeliverData.model_validate(d) for d in data]
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""AuraStream FRED Vintages Model."""
|
|
2
|
+
|
|
3
|
+
from datetime import date as dateType, datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from openbb_core.provider.abstract.data import Data
|
|
7
|
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
|
8
|
+
from openbb_core.provider.standard_models.bls_series import SeriesQueryParams
|
|
9
|
+
from openbb_core.provider.utils.errors import EmptyDataError
|
|
10
|
+
from pydantic import Field
|
|
11
|
+
|
|
12
|
+
from openbb_aurastream.utils.client import (
|
|
13
|
+
api_key,
|
|
14
|
+
default_start,
|
|
15
|
+
fetch,
|
|
16
|
+
parse_timestamp,
|
|
17
|
+
require_key,
|
|
18
|
+
split_symbols,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AuraStreamFredVintagesQueryParams(SeriesQueryParams):
|
|
23
|
+
"""AuraStream FRED Vintages Query.
|
|
24
|
+
|
|
25
|
+
Source: https://aurastream.unbiased-alpha.com/docs/data-sources/fred/
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
|
|
29
|
+
|
|
30
|
+
symbol: str = Field(
|
|
31
|
+
description="FRED series ID, e.g. 'CPIAUCSL', 'UNRATE', 'GDPC1'."
|
|
32
|
+
)
|
|
33
|
+
start_date: dateType | None = Field(
|
|
34
|
+
default=None,
|
|
35
|
+
description="First publication date. Default: one year ago.",
|
|
36
|
+
)
|
|
37
|
+
end_date: dateType | None = Field(
|
|
38
|
+
default=None, description="Last publication date. Default: today."
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class AuraStreamFredVintagesData(Data):
|
|
43
|
+
"""AuraStream FRED Vintages Data. One row per published value."""
|
|
44
|
+
|
|
45
|
+
date: datetime = Field(description="Publication date of the value.")
|
|
46
|
+
symbol: str = Field(description="FRED series ID.")
|
|
47
|
+
reference_date: datetime = Field(description="The period the value describes.")
|
|
48
|
+
value: float | None = Field(default=None, description="The value as published.")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AuraStreamFredVintagesFetcher(
|
|
52
|
+
Fetcher[AuraStreamFredVintagesQueryParams, list[AuraStreamFredVintagesData]]
|
|
53
|
+
):
|
|
54
|
+
"""Fetch every published version of FRED series from AuraStream."""
|
|
55
|
+
|
|
56
|
+
require_credentials = False
|
|
57
|
+
|
|
58
|
+
@staticmethod
|
|
59
|
+
def transform_query(params: dict[str, Any]) -> AuraStreamFredVintagesQueryParams:
|
|
60
|
+
"""Transform the query."""
|
|
61
|
+
return AuraStreamFredVintagesQueryParams(**params)
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
async def aextract_data(
|
|
65
|
+
query: AuraStreamFredVintagesQueryParams,
|
|
66
|
+
credentials: dict[str, str] | None,
|
|
67
|
+
**kwargs: Any,
|
|
68
|
+
) -> list[dict]:
|
|
69
|
+
"""Return one row per (publication date, period)."""
|
|
70
|
+
key = api_key(credentials)
|
|
71
|
+
fred_key = require_key(
|
|
72
|
+
credentials,
|
|
73
|
+
"aurastream_fred_api_key",
|
|
74
|
+
"FRED needs a free key of your own: https://fred.stlouisfed.org/docs/api/api_key.html.",
|
|
75
|
+
)
|
|
76
|
+
symbols = [s.upper() for s in split_symbols(query.symbol)]
|
|
77
|
+
body = {
|
|
78
|
+
"tickerlist": symbols,
|
|
79
|
+
"start": str(query.start_date or default_start()),
|
|
80
|
+
"end": str(query.end_date or dateType.today()),
|
|
81
|
+
"interval": "1d", # ignored: each series comes at its own frequency
|
|
82
|
+
"source": "fred",
|
|
83
|
+
"credentials": {"key": fred_key},
|
|
84
|
+
}
|
|
85
|
+
data = await fetch(body, key)
|
|
86
|
+
|
|
87
|
+
rows: list[dict] = []
|
|
88
|
+
for symbol in symbols:
|
|
89
|
+
series = data.get(symbol)
|
|
90
|
+
if not isinstance(series, dict):
|
|
91
|
+
continue
|
|
92
|
+
for published, row in series.items():
|
|
93
|
+
for update in (row or {}).get("updates") or []:
|
|
94
|
+
rows.append(
|
|
95
|
+
{
|
|
96
|
+
"date": parse_timestamp(published),
|
|
97
|
+
"symbol": symbol,
|
|
98
|
+
"reference_date": parse_timestamp(update["reference_date"]),
|
|
99
|
+
"value": update.get("value"),
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
if not rows:
|
|
103
|
+
raise EmptyDataError(f"AuraStream returned no data for {', '.join(symbols)}.")
|
|
104
|
+
rows.sort(key=lambda r: (r["date"], r["symbol"], r["reference_date"]))
|
|
105
|
+
return rows
|
|
106
|
+
|
|
107
|
+
@staticmethod
|
|
108
|
+
def transform_data(
|
|
109
|
+
query: AuraStreamFredVintagesQueryParams, data: list[dict], **kwargs: Any
|
|
110
|
+
) -> list[AuraStreamFredVintagesData]:
|
|
111
|
+
"""Transform the data."""
|
|
112
|
+
return [AuraStreamFredVintagesData.model_validate(d) for d in data]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""AuraStream Binance Funding Rates Model."""
|
|
2
|
+
|
|
3
|
+
from datetime import date as dateType, datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from openbb_core.provider.abstract.data import Data
|
|
7
|
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
|
8
|
+
from openbb_core.provider.standard_models.bls_series import SeriesQueryParams
|
|
9
|
+
from pydantic import Field
|
|
10
|
+
|
|
11
|
+
from openbb_aurastream.utils.client import (
|
|
12
|
+
api_key,
|
|
13
|
+
default_start,
|
|
14
|
+
fetch,
|
|
15
|
+
split_symbols,
|
|
16
|
+
to_rows,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AuraStreamFundingRatesQueryParams(SeriesQueryParams):
|
|
21
|
+
"""AuraStream Binance Funding Rates Query.
|
|
22
|
+
|
|
23
|
+
Source: https://aurastream.unbiased-alpha.com/docs/data-sources/binance/
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
|
|
27
|
+
|
|
28
|
+
symbol: str = Field(
|
|
29
|
+
default="BTCUSDT",
|
|
30
|
+
description="Binance USD-M perpetual symbol, e.g. 'BTCUSDT', 'ETHUSDT'.",
|
|
31
|
+
)
|
|
32
|
+
start_date: dateType | None = Field(
|
|
33
|
+
default=None, description="Start date. Default: one year ago."
|
|
34
|
+
)
|
|
35
|
+
end_date: dateType | None = Field(
|
|
36
|
+
default=None, description="End date. Default: today."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class AuraStreamFundingRatesData(Data):
|
|
41
|
+
"""AuraStream Binance Funding Rates Data. One row per funding time."""
|
|
42
|
+
|
|
43
|
+
__alias_dict__ = {"funding_rate": "fundingRate"}
|
|
44
|
+
|
|
45
|
+
date: datetime = Field(description="Funding time.")
|
|
46
|
+
symbol: str = Field(description="Perpetual symbol.")
|
|
47
|
+
funding_rate: float | None = Field(
|
|
48
|
+
default=None,
|
|
49
|
+
description="Funding rate for the period, as a fraction (0.0001 = 0.01%).",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class AuraStreamFundingRatesFetcher(
|
|
54
|
+
Fetcher[AuraStreamFundingRatesQueryParams, list[AuraStreamFundingRatesData]]
|
|
55
|
+
):
|
|
56
|
+
"""Fetch Binance perpetual funding rates from AuraStream."""
|
|
57
|
+
|
|
58
|
+
require_credentials = False
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def transform_query(params: dict[str, Any]) -> AuraStreamFundingRatesQueryParams:
|
|
62
|
+
"""Transform the query."""
|
|
63
|
+
return AuraStreamFundingRatesQueryParams(**params)
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
async def aextract_data(
|
|
67
|
+
query: AuraStreamFundingRatesQueryParams,
|
|
68
|
+
credentials: dict[str, str] | None,
|
|
69
|
+
**kwargs: Any,
|
|
70
|
+
) -> list[dict]:
|
|
71
|
+
"""Return the raw rows."""
|
|
72
|
+
symbols = [s.upper() for s in split_symbols(query.symbol)]
|
|
73
|
+
body = {
|
|
74
|
+
"tickerlist": symbols,
|
|
75
|
+
"start": str(query.start_date or default_start()),
|
|
76
|
+
"end": str(query.end_date or dateType.today()),
|
|
77
|
+
"interval": "1h",
|
|
78
|
+
"source": "binance",
|
|
79
|
+
"instrument": "futures",
|
|
80
|
+
"bars": "funding",
|
|
81
|
+
}
|
|
82
|
+
data = await fetch(body, api_key(credentials))
|
|
83
|
+
return to_rows(data, symbols)
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def transform_data(
|
|
87
|
+
query: AuraStreamFundingRatesQueryParams, data: list[dict], **kwargs: Any
|
|
88
|
+
) -> list[AuraStreamFundingRatesData]:
|
|
89
|
+
"""Transform the data."""
|
|
90
|
+
return [AuraStreamFundingRatesData.model_validate(d) for d in data]
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""AuraStream EU Net Short Positions Model."""
|
|
2
|
+
|
|
3
|
+
from datetime import date as dateType, datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from openbb_core.provider.abstract.data import Data
|
|
7
|
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
|
8
|
+
from openbb_core.provider.standard_models.bls_series import SeriesQueryParams
|
|
9
|
+
from pydantic import Field, field_validator
|
|
10
|
+
|
|
11
|
+
from openbb_aurastream.utils.client import (
|
|
12
|
+
api_key,
|
|
13
|
+
default_start,
|
|
14
|
+
fetch,
|
|
15
|
+
split_symbols,
|
|
16
|
+
to_rows,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AuraStreamNetShortQueryParams(SeriesQueryParams):
|
|
21
|
+
"""AuraStream EU Net Short Positions Query.
|
|
22
|
+
|
|
23
|
+
Source: https://aurastream.unbiased-alpha.com/docs/data-sources/netshort/
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
__json_schema_extra__ = {
|
|
27
|
+
"symbol": {"multiple_items_allowed": True},
|
|
28
|
+
"country": {"multiple_items_allowed": True, "choices": ["FR", "NL", "ES"]},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
symbol: str = Field(
|
|
32
|
+
description="ISIN (e.g. 'FR0000054470'), part of the issuer's name (e.g. 'ubisoft'),"
|
|
33
|
+
+ " or 'all' for every position in the period.",
|
|
34
|
+
)
|
|
35
|
+
start_date: dateType | None = Field(
|
|
36
|
+
default=None, description="Start date. Default: one year ago."
|
|
37
|
+
)
|
|
38
|
+
end_date: dateType | None = Field(
|
|
39
|
+
default=None, description="End date. Default: today."
|
|
40
|
+
)
|
|
41
|
+
country: str | None = Field(
|
|
42
|
+
default=None,
|
|
43
|
+
description="Registers to search: FR (AMF), NL (AFM), ES (CNMV). Default: all three.",
|
|
44
|
+
)
|
|
45
|
+
holder: str | None = Field(
|
|
46
|
+
default=None,
|
|
47
|
+
description="Keep positions whose holder name contains this text, e.g. 'citadel'.",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@field_validator("symbol", mode="before", check_fields=False)
|
|
52
|
+
@classmethod
|
|
53
|
+
def to_upper(cls, v: str) -> str:
|
|
54
|
+
"""Keep the symbol as written: these tickers are case-sensitive."""
|
|
55
|
+
return v
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class AuraStreamNetShortData(Data):
|
|
59
|
+
"""AuraStream EU Net Short Positions Data. One row per published position."""
|
|
60
|
+
|
|
61
|
+
date: datetime = Field(description="Position date.")
|
|
62
|
+
symbol: str = Field(description="The symbol as requested.")
|
|
63
|
+
holder: str | None = Field(default=None, description="Position holder.")
|
|
64
|
+
issuer: str | None = Field(default=None, description="Company name.")
|
|
65
|
+
isin: str | None = Field(default=None, description="Company ISIN.")
|
|
66
|
+
net_short_pct: float | None = Field(
|
|
67
|
+
default=None,
|
|
68
|
+
description="Net short position, in percent of issued share capital (0.62 = 0.62%).",
|
|
69
|
+
)
|
|
70
|
+
country: str | None = Field(default=None, description="FR, NL or ES.")
|
|
71
|
+
holder_lei: str | None = Field(
|
|
72
|
+
default=None, description="Holder's Legal Entity Identifier (France only)."
|
|
73
|
+
)
|
|
74
|
+
published_from: str | None = Field(
|
|
75
|
+
default=None, description="First date on the public register (France only)."
|
|
76
|
+
)
|
|
77
|
+
published_to: str | None = Field(
|
|
78
|
+
default=None, description="Last date on the public register (France only)."
|
|
79
|
+
)
|
|
80
|
+
source: str | None = Field(
|
|
81
|
+
default=None, description="The attribution the regulator requires."
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class AuraStreamNetShortFetcher(
|
|
86
|
+
Fetcher[AuraStreamNetShortQueryParams, list[AuraStreamNetShortData]]
|
|
87
|
+
):
|
|
88
|
+
"""Fetch EU net short positions from AuraStream."""
|
|
89
|
+
|
|
90
|
+
require_credentials = False
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def transform_query(params: dict[str, Any]) -> AuraStreamNetShortQueryParams:
|
|
94
|
+
"""Transform the query."""
|
|
95
|
+
return AuraStreamNetShortQueryParams(**params)
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
async def aextract_data(
|
|
99
|
+
query: AuraStreamNetShortQueryParams,
|
|
100
|
+
credentials: dict[str, str] | None,
|
|
101
|
+
**kwargs: Any,
|
|
102
|
+
) -> list[dict]:
|
|
103
|
+
"""Return the raw rows."""
|
|
104
|
+
symbols = split_symbols(query.symbol)
|
|
105
|
+
body: dict[str, Any] = {
|
|
106
|
+
"tickerlist": symbols,
|
|
107
|
+
"start": str(query.start_date or default_start()),
|
|
108
|
+
"end": str(query.end_date or dateType.today()),
|
|
109
|
+
"interval": "1d",
|
|
110
|
+
"source": "netshort",
|
|
111
|
+
}
|
|
112
|
+
if query.country:
|
|
113
|
+
body["country"] = [c.upper() for c in split_symbols(query.country)]
|
|
114
|
+
if query.holder:
|
|
115
|
+
body["holder"] = query.holder
|
|
116
|
+
data = await fetch(body, api_key(credentials))
|
|
117
|
+
return to_rows(data, symbols)
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def transform_data(
|
|
121
|
+
query: AuraStreamNetShortQueryParams, data: list[dict], **kwargs: Any
|
|
122
|
+
) -> list[AuraStreamNetShortData]:
|
|
123
|
+
"""Transform the data."""
|
|
124
|
+
return [AuraStreamNetShortData.model_validate(d) for d in data]
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""AuraStream USDA WASDE Model."""
|
|
2
|
+
|
|
3
|
+
from datetime import date as dateType, datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from openbb_core.provider.abstract.data import Data
|
|
7
|
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
|
8
|
+
from openbb_core.provider.standard_models.bls_series import SeriesQueryParams
|
|
9
|
+
from pydantic import Field, field_validator
|
|
10
|
+
|
|
11
|
+
from openbb_aurastream.utils.client import (
|
|
12
|
+
api_key,
|
|
13
|
+
default_start,
|
|
14
|
+
fetch,
|
|
15
|
+
split_symbols,
|
|
16
|
+
to_rows,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AuraStreamWasdeQueryParams(SeriesQueryParams):
|
|
21
|
+
"""AuraStream USDA WASDE Query.
|
|
22
|
+
|
|
23
|
+
Source: https://aurastream.unbiased-alpha.com/docs/data-sources/wasde/
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
|
|
27
|
+
|
|
28
|
+
symbol: str = Field(
|
|
29
|
+
description="'commodity:attribute' for the US balance sheet (e.g. 'corn:ending_stocks'),"
|
|
30
|
+
+ " or 'commodity:attribute:region' for the world tables (e.g. 'soybeans:production:Brazil').",
|
|
31
|
+
)
|
|
32
|
+
start_date: dateType | None = Field(
|
|
33
|
+
default=None, description="Start date. Default: one year ago."
|
|
34
|
+
)
|
|
35
|
+
end_date: dateType | None = Field(
|
|
36
|
+
default=None, description="End date. Default: today."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@field_validator("symbol", mode="before", check_fields=False)
|
|
41
|
+
@classmethod
|
|
42
|
+
def to_upper(cls, v: str) -> str:
|
|
43
|
+
"""Keep the symbol as written: these tickers are case-sensitive."""
|
|
44
|
+
return v
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AuraStreamWasdeData(Data):
|
|
48
|
+
"""AuraStream USDA WASDE Data. One row per report."""
|
|
49
|
+
|
|
50
|
+
date: datetime = Field(description="Report release time (12:00 New York).")
|
|
51
|
+
symbol: str = Field(description="The series as requested.")
|
|
52
|
+
report: str | None = Field(
|
|
53
|
+
default=None, description="Report month, e.g. 'October 2026'."
|
|
54
|
+
)
|
|
55
|
+
market_year: str | None = Field(
|
|
56
|
+
default=None, description="Marketing year projected, e.g. '2026/27'."
|
|
57
|
+
)
|
|
58
|
+
value: float | None = Field(default=None, description="This report's projection.")
|
|
59
|
+
previous: float | None = Field(
|
|
60
|
+
default=None,
|
|
61
|
+
description="The previous month's projection, as printed in the same report.",
|
|
62
|
+
)
|
|
63
|
+
prior_year: str | None = Field(default=None, description="The year before.")
|
|
64
|
+
prior_year_value: float | None = Field(
|
|
65
|
+
default=None, description="The estimate for the year before."
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class AuraStreamWasdeFetcher(
|
|
70
|
+
Fetcher[AuraStreamWasdeQueryParams, list[AuraStreamWasdeData]]
|
|
71
|
+
):
|
|
72
|
+
"""Fetch WASDE projections from AuraStream."""
|
|
73
|
+
|
|
74
|
+
require_credentials = False
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def transform_query(params: dict[str, Any]) -> AuraStreamWasdeQueryParams:
|
|
78
|
+
"""Transform the query."""
|
|
79
|
+
return AuraStreamWasdeQueryParams(**params)
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
async def aextract_data(
|
|
83
|
+
query: AuraStreamWasdeQueryParams,
|
|
84
|
+
credentials: dict[str, str] | None,
|
|
85
|
+
**kwargs: Any,
|
|
86
|
+
) -> list[dict]:
|
|
87
|
+
"""Return the raw rows."""
|
|
88
|
+
symbols = split_symbols(query.symbol)
|
|
89
|
+
body = {
|
|
90
|
+
"tickerlist": symbols,
|
|
91
|
+
"start": str(query.start_date or default_start()),
|
|
92
|
+
"end": str(query.end_date or dateType.today()),
|
|
93
|
+
"interval": "1m",
|
|
94
|
+
"source": "wasde",
|
|
95
|
+
}
|
|
96
|
+
data = await fetch(body, api_key(credentials))
|
|
97
|
+
return to_rows(data, symbols)
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def transform_data(
|
|
101
|
+
query: AuraStreamWasdeQueryParams, data: list[dict], **kwargs: Any
|
|
102
|
+
) -> list[AuraStreamWasdeData]:
|
|
103
|
+
"""Transform the data."""
|
|
104
|
+
return [AuraStreamWasdeData.model_validate(d) for d in data]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""AuraStream utilities."""
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Thin async client for the AuraStream /timeseries endpoint."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from datetime import date, datetime, timedelta
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from openbb_core.app.model.abstract.error import OpenBBError
|
|
8
|
+
from openbb_core.provider.utils.errors import EmptyDataError, UnauthorizedError
|
|
9
|
+
|
|
10
|
+
API_URL = os.environ.get(
|
|
11
|
+
"AURASTREAM_API_URL", "https://api.aurastream.unbiased-alpha.com/timeseries"
|
|
12
|
+
)
|
|
13
|
+
DASHBOARD_URL = "https://aurastream.unbiased-alpha.com"
|
|
14
|
+
TIMEOUT_SECONDS = 130 # the API itself times out at 120 s
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def default_start() -> date:
|
|
18
|
+
"""One year back, the default start of a query."""
|
|
19
|
+
return date.today() - timedelta(days=365)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def require_key(credentials: dict[str, str] | None, name: str, hint: str) -> str:
|
|
23
|
+
"""Return a credential or raise an error saying how to set it."""
|
|
24
|
+
value = (credentials or {}).get(name)
|
|
25
|
+
if not value:
|
|
26
|
+
raise UnauthorizedError(
|
|
27
|
+
f"Missing credential '{name}'. {hint} Set it with "
|
|
28
|
+
f'obb.user.credentials.{name} = "..." or in ~/.openbb_platform/user_settings.json.'
|
|
29
|
+
)
|
|
30
|
+
return value
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def api_key(credentials: dict[str, str] | None) -> str:
|
|
34
|
+
"""The AuraStream API key."""
|
|
35
|
+
return require_key(
|
|
36
|
+
credentials,
|
|
37
|
+
"aurastream_api_key",
|
|
38
|
+
f"Get a free key (500 calls a month, no credit card) at {DASHBOARD_URL}/sign-up.",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _error_message(status: int, detail: str) -> str:
|
|
43
|
+
if status == 429:
|
|
44
|
+
if "limit exceeded" in detail.lower():
|
|
45
|
+
return (
|
|
46
|
+
"AuraStream monthly call allowance is used up. It resets on the 1st of next month "
|
|
47
|
+
f"(00:00 UTC); to keep going now, upgrade the plan at {DASHBOARD_URL}."
|
|
48
|
+
)
|
|
49
|
+
return "AuraStream rate limit hit. Wait a few seconds and retry."
|
|
50
|
+
if status in (401, 403):
|
|
51
|
+
return (
|
|
52
|
+
f"AuraStream rejected the API key (HTTP {status}). "
|
|
53
|
+
f"Check aurastream_api_key; keys are on the dashboard at {DASHBOARD_URL}."
|
|
54
|
+
)
|
|
55
|
+
if status == 400:
|
|
56
|
+
return f"AuraStream rejected the request (HTTP 400): {detail or 'no detail'}."
|
|
57
|
+
if status in (502, 504):
|
|
58
|
+
return (
|
|
59
|
+
f"AuraStream timed out (HTTP {status}). "
|
|
60
|
+
"Try a shorter date range or fewer symbols."
|
|
61
|
+
)
|
|
62
|
+
return f"AuraStream returned HTTP {status}: {detail or 'no detail'}"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
async def fetch(body: dict[str, Any], key: str) -> dict[str, Any]:
|
|
66
|
+
"""POST the body to AuraStream and return the parsed JSON."""
|
|
67
|
+
import aiohttp
|
|
68
|
+
|
|
69
|
+
timeout = aiohttp.ClientTimeout(total=TIMEOUT_SECONDS)
|
|
70
|
+
try:
|
|
71
|
+
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
72
|
+
async with session.post(
|
|
73
|
+
API_URL, json=body, headers={"x-api-key": key}
|
|
74
|
+
) as resp:
|
|
75
|
+
status = resp.status
|
|
76
|
+
text = await resp.text()
|
|
77
|
+
except TimeoutError:
|
|
78
|
+
raise OpenBBError(
|
|
79
|
+
"AuraStream did not answer in time. Try a shorter date range or fewer symbols."
|
|
80
|
+
) from None
|
|
81
|
+
except aiohttp.ClientError as exc:
|
|
82
|
+
raise OpenBBError(f"Could not reach AuraStream: {exc}") from None
|
|
83
|
+
|
|
84
|
+
import json
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
data = json.loads(text)
|
|
88
|
+
except ValueError:
|
|
89
|
+
data = None
|
|
90
|
+
|
|
91
|
+
if status != 200:
|
|
92
|
+
detail = ""
|
|
93
|
+
if isinstance(data, dict):
|
|
94
|
+
detail = str(
|
|
95
|
+
data.get("message") or data.get("error") or data.get("detail") or ""
|
|
96
|
+
)
|
|
97
|
+
elif text:
|
|
98
|
+
detail = text.strip()
|
|
99
|
+
message = _error_message(status, detail[:300])
|
|
100
|
+
if status in (401, 403):
|
|
101
|
+
raise UnauthorizedError(message)
|
|
102
|
+
raise OpenBBError(message)
|
|
103
|
+
|
|
104
|
+
if not isinstance(data, dict):
|
|
105
|
+
raise OpenBBError("AuraStream returned a response that is not a JSON object.")
|
|
106
|
+
# Errors that come back with a 200, e.g. {"error": "..."}.
|
|
107
|
+
if len(data) == 1:
|
|
108
|
+
k, v = next(iter(data.items()))
|
|
109
|
+
if k in ("error", "message") and isinstance(v, str):
|
|
110
|
+
raise OpenBBError(f"AuraStream error: {v[:300]}")
|
|
111
|
+
return data
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def parse_timestamp(value: str) -> datetime:
|
|
115
|
+
"""Parse a row key such as '2025-02-12 00:00:00' or '2025-02-12 00:00:00.000001'."""
|
|
116
|
+
return datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def to_rows(data: dict[str, Any], symbols: list[str]) -> list[dict[str, Any]]:
|
|
120
|
+
"""Flatten {symbol: {timestamp: fields}} into rows with 'date' and 'symbol'.
|
|
121
|
+
|
|
122
|
+
Raises EmptyDataError when no symbol returned any row.
|
|
123
|
+
"""
|
|
124
|
+
rows: list[dict[str, Any]] = []
|
|
125
|
+
errors: list[str] = []
|
|
126
|
+
for symbol in symbols:
|
|
127
|
+
series = data.get(symbol)
|
|
128
|
+
if isinstance(series, str):
|
|
129
|
+
errors.append(f"{symbol}: {series[:200]}")
|
|
130
|
+
continue
|
|
131
|
+
if not isinstance(series, dict):
|
|
132
|
+
continue
|
|
133
|
+
for ts, fields in series.items():
|
|
134
|
+
if not isinstance(fields, dict):
|
|
135
|
+
continue
|
|
136
|
+
row = {"date": parse_timestamp(ts), "symbol": symbol}
|
|
137
|
+
row.update(fields)
|
|
138
|
+
rows.append(row)
|
|
139
|
+
if not rows:
|
|
140
|
+
extra = f" ({'; '.join(errors)})" if errors else ""
|
|
141
|
+
raise EmptyDataError(f"AuraStream returned no data for {', '.join(symbols)}{extra}.")
|
|
142
|
+
rows.sort(key=lambda r: (r["date"], r["symbol"]))
|
|
143
|
+
return rows
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def split_symbols(symbol: str) -> list[str]:
|
|
147
|
+
"""'a, b' -> ['a', 'b']."""
|
|
148
|
+
return [s.strip() for s in symbol.split(",") if s.strip()]
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: openbb-aurastream
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: AuraStream data for the OpenBB Platform: EU net short positions, USDA WASDE, FRED vintages, SEC fails-to-deliver and Binance funding rates.
|
|
5
|
+
Project-URL: Homepage, https://aurastream.unbiased-alpha.com
|
|
6
|
+
Project-URL: Documentation, https://aurastream.unbiased-alpha.com/docs/
|
|
7
|
+
Project-URL: Source, https://github.com/UnbiasedAlpha/openbb-aurastream
|
|
8
|
+
Author: Unbiased Alpha
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: aurastream,fred,market data,openbb,quant,short selling,wasde
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Requires-Dist: aiohttp>=3.9
|
|
14
|
+
Requires-Dist: openbb-core<3,>=2.0
|
|
15
|
+
Provides-Extra: test
|
|
16
|
+
Requires-Dist: pytest>=8; extra == 'test'
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# openbb-aurastream
|
|
20
|
+
|
|
21
|
+
[AuraStream](https://aurastream.unbiased-alpha.com) data for the [OpenBB Platform](https://github.com/OpenBB-finance/OpenBB). The extension adds five commands under `obb.aurastream`. Each covers a dataset that the other OpenBB providers don't offer:
|
|
22
|
+
|
|
23
|
+
| Command | Data |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `net_short` | EU net short positions of 0.5% or more, with the holder's name, from the French (AMF), Dutch (AFM) and Spanish (CNMV) registers, back to 2010–2012 |
|
|
26
|
+
| `wasde` | USDA WASDE projections since July 2010: US balance sheets and world tables by country, one row per monthly report |
|
|
27
|
+
| `fred_vintages` | FRED series with every revision, stamped at the date each value was published |
|
|
28
|
+
| `fails_to_deliver` | SEC fails-to-deliver for any US stock or ETF |
|
|
29
|
+
| `funding_rates` | Binance USD-M perpetual funding rates |
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
pip install openbb openbb-aurastream
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
OpenBB rebuilds its Python interface the first time you import it after installing.
|
|
38
|
+
|
|
39
|
+
## Credentials
|
|
40
|
+
|
|
41
|
+
You need an AuraStream API key. The free plan includes 500 calls a month, with no credit card required: [sign up here](https://aurastream.unbiased-alpha.com/sign-up).
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from openbb import obb
|
|
45
|
+
|
|
46
|
+
obb.user.credentials.aurastream_api_key = "..."
|
|
47
|
+
# Only for fred_vintages: your own free FRED key (https://fred.stlouisfed.org/docs/api/api_key.html)
|
|
48
|
+
obb.user.credentials.aurastream_fred_api_key = "..."
|
|
49
|
+
# Only for fails_to_deliver: the SEC asks automated requests to name the sender
|
|
50
|
+
obb.user.credentials.aurastream_sec_user_agent = "Jane Doe jane@example.com"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
You can also keep them in `~/.openbb_platform/user_settings.json`.
|
|
54
|
+
|
|
55
|
+
## Examples
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from openbb import obb
|
|
59
|
+
|
|
60
|
+
# Who is short Ubisoft, and by how much
|
|
61
|
+
shorts = obb.aurastream.net_short("ubisoft", start_date="2024-01-01", country="FR").to_df()
|
|
62
|
+
shorts.pivot_table(index=shorts.index.date, columns="holder", values="net_short_pct").ffill()
|
|
63
|
+
|
|
64
|
+
# Every position one fund has disclosed
|
|
65
|
+
obb.aurastream.net_short("all", holder="citadel", start_date="2026-01-01").to_df()
|
|
66
|
+
|
|
67
|
+
# How the USDA's projection of US corn ending stocks changed, report by report
|
|
68
|
+
corn = obb.aurastream.wasde("corn:ending_stocks", start_date="2024-01-01").to_df()
|
|
69
|
+
corn["revision"] = corn["value"] - corn["previous"]
|
|
70
|
+
|
|
71
|
+
# Brazil's soybean crop from the world tables
|
|
72
|
+
obb.aurastream.wasde("soybeans:production:Brazil").to_df()
|
|
73
|
+
|
|
74
|
+
# CPI as first published vs. after revisions
|
|
75
|
+
cpi = obb.aurastream.fred_vintages("CPIAUCSL", start_date="2020-01-01").to_df()
|
|
76
|
+
first_print = cpi.groupby("reference_date")["value"].first()
|
|
77
|
+
latest = cpi.groupby("reference_date")["value"].last()
|
|
78
|
+
|
|
79
|
+
# Fails-to-deliver in GameStop
|
|
80
|
+
obb.aurastream.fails_to_deliver("GME", start_date="2024-01-01", end_date="2025-01-01").to_df()
|
|
81
|
+
|
|
82
|
+
# Funding rates of the Bitcoin and Ether perpetuals
|
|
83
|
+
obb.aurastream.funding_rates("BTCUSDT,ETHUSDT", start_date="2025-06-01").to_df()
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Without `start_date`, each command returns the last year.
|
|
87
|
+
|
|
88
|
+
## Timing
|
|
89
|
+
|
|
90
|
+
All five datasets are historical, not real-time, and each row is stamped when the data became public:
|
|
91
|
+
|
|
92
|
+
- EU net short positions are published by the next trading day, so lag them by one day in a backtest.
|
|
93
|
+
- WASDE rows are stamped at the report's release, 12:00 New York time.
|
|
94
|
+
- For `fred_vintages`, `start_date` and `end_date` filter on the publication date.
|
|
95
|
+
- The SEC publishes fails-to-deliver twice a month, about two weeks after each half-month ends.
|
|
96
|
+
|
|
97
|
+
See the [AuraStream docs](https://aurastream.unbiased-alpha.com/docs/) for the field definitions and coverage of each source.
|
|
98
|
+
|
|
99
|
+
## Data terms
|
|
100
|
+
|
|
101
|
+
- France: source Autorité des marchés financiers (AMF), data.gouv.fr, Licence Ouverte 2.0.
|
|
102
|
+
- Netherlands: source AFM.
|
|
103
|
+
- Spain: this information can be obtained free of charge from the CNMV website (www.cnmv.es).
|
|
104
|
+
- Holder names can belong to individuals, so treat them as personal data under the GDPR.
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
MIT. This package is by Unbiased Alpha and is not affiliated with OpenBB.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
openbb_aurastream/__init__.py,sha256=ncT7sOUpQ79J6msgiqz_Txf-2E2DfK_7FWKe9DzkctI,1622
|
|
2
|
+
openbb_aurastream/aurastream_router.py,sha256=TOjQq_Xs14KDBvAVWQAt4XxqwAFExPKwcQVD5SFaIjw,3700
|
|
3
|
+
openbb_aurastream/models/__init__.py,sha256=PTHyuY9uRh3ZtnbI_7kRQZSCDvabO3-MYWc8suSLkB8,25
|
|
4
|
+
openbb_aurastream/models/fails_to_deliver.py,sha256=xk15CRydka3BPIdGL8ek7yk2PGg0yLlGEvKT5YqugGo,3424
|
|
5
|
+
openbb_aurastream/models/fred_vintages.py,sha256=PTXuTomqQDPj6TQkquK7TRuMCopXetcCVsZ8L3jS10I,4068
|
|
6
|
+
openbb_aurastream/models/funding_rates.py,sha256=s6RXyvdo_SDshiIENd8y7mDrwHCG0qFH26j4IAGVpwk,2924
|
|
7
|
+
openbb_aurastream/models/net_short.py,sha256=lACSVBl4QaBBHJFWsRDypIU8oMNTEId7E-QRHPS3l1U,4426
|
|
8
|
+
openbb_aurastream/models/wasde.py,sha256=qQXkXwem_u0Ea-yNnidrGUZ9Bg9VS0JPk4B8CyMY1R8,3521
|
|
9
|
+
openbb_aurastream/utils/__init__.py,sha256=fcXDU1Q3QrZ8YUDBSBmDRyaMWeMcXWp1K00qr0txNPQ,28
|
|
10
|
+
openbb_aurastream/utils/client.py,sha256=Oo5cG_AAhEbQH1nZBTOhpvDdmSnqtkBvWvdmUIPBMIo,5355
|
|
11
|
+
openbb_aurastream-0.1.0.dist-info/METADATA,sha256=DIdERbM4BXX5KTCAi_f3YvZw0Kv6HWxa-utIh4bW010,4650
|
|
12
|
+
openbb_aurastream-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
13
|
+
openbb_aurastream-0.1.0.dist-info/entry_points.txt,sha256=f_A7WUSp88inDmSAUFWeNiGTGx83MperMel3F6wWsWI,160
|
|
14
|
+
openbb_aurastream-0.1.0.dist-info/licenses/LICENSE,sha256=PXINVVvEqW2nfyyKueaoj_IoKs-luXnuyQ0uef99PKY,1071
|
|
15
|
+
openbb_aurastream-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Unbiased Alpha
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|