pyalloq-data-connector 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyalloq_data_connector-0.1.4/PKG-INFO +61 -0
- pyalloq_data_connector-0.1.4/README.md +37 -0
- pyalloq_data_connector-0.1.4/pyproject.toml +60 -0
- pyalloq_data_connector-0.1.4/src/pyalloq_data_connector/__init__.py +0 -0
- pyalloq_data_connector-0.1.4/src/pyalloq_data_connector/alpha_vantage.py +46 -0
- pyalloq_data_connector-0.1.4/src/pyalloq_data_connector/base.py +100 -0
- pyalloq_data_connector-0.1.4/src/pyalloq_data_connector/eod.py +45 -0
- pyalloq_data_connector-0.1.4/src/pyalloq_data_connector/finnhub.py +56 -0
- pyalloq_data_connector-0.1.4/src/pyalloq_data_connector/py.typed +1 -0
- pyalloq_data_connector-0.1.4/src/pyalloq_data_connector/yahoo_finance.py +36 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: pyalloq-data-connector
|
|
3
|
+
Version: 0.1.4
|
|
4
|
+
Summary: Vendor-agnostic data adapters for PyAlloq.
|
|
5
|
+
Author: Siddeshkanth
|
|
6
|
+
Author-email: Siddeshkanth <pyalloq-info@alloq-alpha.com>
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
13
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
14
|
+
Requires-Dist: numpy>=1.24
|
|
15
|
+
Requires-Dist: pandas>=2.0
|
|
16
|
+
Requires-Dist: yfinance>=1.6.0
|
|
17
|
+
Requires-Dist: requests>=2.31
|
|
18
|
+
Requires-Dist: pyalloq-core
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Project-URL: Homepage, https://github.com/your-org/qpo
|
|
21
|
+
Project-URL: Repository, https://github.com/your-org/qpo
|
|
22
|
+
Project-URL: Issues, https://github.com/your-org/qpo/issues
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# pyalloq-data-connector
|
|
26
|
+
|
|
27
|
+
`pyalloq-data-connector` provides vendor-agnostic data adapters for **PyAlloq**. It handles fetching raw market data from various third-party APIs and automatically standardizes them into pure, time-aligned `MarketData` objects.
|
|
28
|
+
|
|
29
|
+
## Supported Adapters
|
|
30
|
+
|
|
31
|
+
- **`YahooFinanceClient`**: Free adapter using Yahoo Finance (`yfinance`). No API key required.
|
|
32
|
+
- **`AlphaVantageClient`**: Adapter for Alpha Vantage Time Series Daily API.
|
|
33
|
+
- **`FinnhubClient`**: Adapter for Finnhub's stock candle endpoint.
|
|
34
|
+
- **`EODHistoricalDataClient`**: Adapter for EOD Historical Data API.
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
- **Standardized Output**: Automatically converts heterogeneous JSON/DataFrame vendor payloads into `T x N` aligned price matrices (`MarketData`).
|
|
39
|
+
- **Missing Value Handling**: Implements forward filling (`ffill`) for prices and zero-filling for volume data.
|
|
40
|
+
- **Time Alignment**: Constructs unified datetime indices across all requested ticker symbols.
|
|
41
|
+
|
|
42
|
+
## Quick Example
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import datetime as dt
|
|
46
|
+
from pyalloq_data_connector.yahoo_finance import YahooFinanceClient
|
|
47
|
+
|
|
48
|
+
client = YahooFinanceClient()
|
|
49
|
+
start = dt.datetime(2023, 1, 1)
|
|
50
|
+
end = dt.datetime(2024, 1, 1)
|
|
51
|
+
|
|
52
|
+
# Fetch standardized MarketData object
|
|
53
|
+
market_data = client.get_market_data(
|
|
54
|
+
tickers=["AAPL", "MSFT", "GOOGL"],
|
|
55
|
+
start=start,
|
|
56
|
+
end=end
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
print(market_data.prices.head())
|
|
60
|
+
print(market_data.features["volume"].head())
|
|
61
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# pyalloq-data-connector
|
|
2
|
+
|
|
3
|
+
`pyalloq-data-connector` provides vendor-agnostic data adapters for **PyAlloq**. It handles fetching raw market data from various third-party APIs and automatically standardizes them into pure, time-aligned `MarketData` objects.
|
|
4
|
+
|
|
5
|
+
## Supported Adapters
|
|
6
|
+
|
|
7
|
+
- **`YahooFinanceClient`**: Free adapter using Yahoo Finance (`yfinance`). No API key required.
|
|
8
|
+
- **`AlphaVantageClient`**: Adapter for Alpha Vantage Time Series Daily API.
|
|
9
|
+
- **`FinnhubClient`**: Adapter for Finnhub's stock candle endpoint.
|
|
10
|
+
- **`EODHistoricalDataClient`**: Adapter for EOD Historical Data API.
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **Standardized Output**: Automatically converts heterogeneous JSON/DataFrame vendor payloads into `T x N` aligned price matrices (`MarketData`).
|
|
15
|
+
- **Missing Value Handling**: Implements forward filling (`ffill`) for prices and zero-filling for volume data.
|
|
16
|
+
- **Time Alignment**: Constructs unified datetime indices across all requested ticker symbols.
|
|
17
|
+
|
|
18
|
+
## Quick Example
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
import datetime as dt
|
|
22
|
+
from pyalloq_data_connector.yahoo_finance import YahooFinanceClient
|
|
23
|
+
|
|
24
|
+
client = YahooFinanceClient()
|
|
25
|
+
start = dt.datetime(2023, 1, 1)
|
|
26
|
+
end = dt.datetime(2024, 1, 1)
|
|
27
|
+
|
|
28
|
+
# Fetch standardized MarketData object
|
|
29
|
+
market_data = client.get_market_data(
|
|
30
|
+
tickers=["AAPL", "MSFT", "GOOGL"],
|
|
31
|
+
start=start,
|
|
32
|
+
end=end
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
print(market_data.prices.head())
|
|
36
|
+
print(market_data.features["volume"].head())
|
|
37
|
+
```
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build>=0.10.9,<0.11.0"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pyalloq-data-connector"
|
|
7
|
+
version = "0.1.4"
|
|
8
|
+
description = "Vendor-agnostic data adapters for PyAlloq."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [
|
|
11
|
+
{ name = "Siddeshkanth", email = "pyalloq-info@alloq-alpha.com" }
|
|
12
|
+
]
|
|
13
|
+
requires-python = ">=3.11"
|
|
14
|
+
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
21
|
+
"Topic :: Scientific/Engineering :: Mathematics",
|
|
22
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
dependencies = [
|
|
26
|
+
"numpy>=1.24",
|
|
27
|
+
"pandas>=2.0",
|
|
28
|
+
"yfinance>=1.6.0",
|
|
29
|
+
"requests>=2.31",
|
|
30
|
+
"pyalloq-core"
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = [
|
|
35
|
+
"pytest>=8",
|
|
36
|
+
"pytest-cov>=5",
|
|
37
|
+
"ruff>=0.6",
|
|
38
|
+
"mypy>=1.11",
|
|
39
|
+
"types-requests>=2.0",
|
|
40
|
+
"pandas-stubs>=3.0.5.260730",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[project.urls]
|
|
44
|
+
Homepage = "https://github.com/your-org/qpo"
|
|
45
|
+
Repository = "https://github.com/your-org/qpo"
|
|
46
|
+
Issues = "https://github.com/your-org/qpo/issues"
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.packages.find]
|
|
49
|
+
where = ["src/"]
|
|
50
|
+
|
|
51
|
+
[[tool.mypy.overrides]]
|
|
52
|
+
module = [
|
|
53
|
+
"numpy.*",
|
|
54
|
+
"pandas.*",
|
|
55
|
+
"yfinance.*"
|
|
56
|
+
]
|
|
57
|
+
ignore_missing_imports = true
|
|
58
|
+
|
|
59
|
+
[tool.uv.sources]
|
|
60
|
+
pyalloq-core = { workspace = true }
|
|
File without changes
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import datetime as dt
|
|
4
|
+
from pyalloq_data_connector.base import BaseDataClient
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AlphaVantageClient(BaseDataClient):
|
|
8
|
+
"""Adapter for Alpha Vantage Time Series Daily."""
|
|
9
|
+
|
|
10
|
+
BASE_URL = "https://www.alphavantage.co/query"
|
|
11
|
+
|
|
12
|
+
def fetch_raw_data(
|
|
13
|
+
self, tickers: list[str], start: dt.datetime, end: dt.datetime
|
|
14
|
+
) -> dict[str, pd.DataFrame]:
|
|
15
|
+
if not self.api_key:
|
|
16
|
+
raise ValueError("Alpha Vantage requires an API key.")
|
|
17
|
+
|
|
18
|
+
raw_dict = {}
|
|
19
|
+
start_ts = pd.Timestamp(start)
|
|
20
|
+
end_ts = pd.Timestamp(end)
|
|
21
|
+
|
|
22
|
+
for ticker in tickers:
|
|
23
|
+
params = {
|
|
24
|
+
"function": "TIME_SERIES_DAILY",
|
|
25
|
+
"symbol": ticker,
|
|
26
|
+
"outputsize": "full",
|
|
27
|
+
"apikey": self.api_key,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
response = requests.get(self.BASE_URL, params=params)
|
|
31
|
+
data = response.json()
|
|
32
|
+
|
|
33
|
+
if "Time Series (Daily)" not in data:
|
|
34
|
+
print(f"Warning: Alpha Vantage failed for {ticker}. Check API limits.")
|
|
35
|
+
continue
|
|
36
|
+
|
|
37
|
+
df = pd.DataFrame.from_dict(data["Time Series (Daily)"], orient="index")
|
|
38
|
+
df.index = pd.to_datetime(df.index)
|
|
39
|
+
|
|
40
|
+
df.columns = [col.split(" ")[1] for col in df.columns]
|
|
41
|
+
df = df.astype(float)
|
|
42
|
+
|
|
43
|
+
mask = (df.index >= start_ts) & (df.index <= end_ts)
|
|
44
|
+
raw_dict[ticker] = df.loc[mask].sort_index()
|
|
45
|
+
|
|
46
|
+
return raw_dict
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from pyalloq_core.data import MarketData
|
|
4
|
+
import datetime as dt
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BaseDataClient(ABC):
|
|
8
|
+
"""
|
|
9
|
+
Abstract base class for all third-party data providers.
|
|
10
|
+
Ensures raw API data is always converted to MarketData format.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, api_key: str | None = None):
|
|
14
|
+
self.api_key = api_key
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def fetch_raw_data(
|
|
18
|
+
self,
|
|
19
|
+
tickers: list[str],
|
|
20
|
+
start_date: dt.datetime,
|
|
21
|
+
end_date: dt.datetime,
|
|
22
|
+
) -> dict[str, pd.DataFrame]:
|
|
23
|
+
"""
|
|
24
|
+
Fetches OHLCV data from the provider and packages it into MarketData.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
tickers: List of asset symbols (e.g., ['AAPL', 'MSFT'])
|
|
28
|
+
start_date: YYYY-MM-DD string
|
|
29
|
+
end_date: YYYY-MM-DD string
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
MarketData: The standardized PyAlloq data container.
|
|
33
|
+
"""
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
def get_market_data(
|
|
37
|
+
self,
|
|
38
|
+
tickers: list[str],
|
|
39
|
+
start: dt.datetime,
|
|
40
|
+
end: dt.datetime,
|
|
41
|
+
) -> MarketData:
|
|
42
|
+
"""
|
|
43
|
+
The orchestrator method. Standardizes whatever chaotic data the vendor
|
|
44
|
+
returns into mathematically pure T x N matrices, perfectly aligned in time.
|
|
45
|
+
"""
|
|
46
|
+
raw_dict = self.fetch_raw_data(tickers, start, end)
|
|
47
|
+
|
|
48
|
+
closes, volumes, opens, highs, lows = {}, {}, {}, {}, {}
|
|
49
|
+
all_dates = pd.DatetimeIndex([])
|
|
50
|
+
|
|
51
|
+
for ticker, df in raw_dict.items():
|
|
52
|
+
df.columns = [str(c).lower() for c in df.columns]
|
|
53
|
+
|
|
54
|
+
closes[ticker] = df.get("close", df.get("adj close"))
|
|
55
|
+
if "volume" in df.columns:
|
|
56
|
+
volumes[ticker] = df["volume"]
|
|
57
|
+
if "open" in df.columns:
|
|
58
|
+
opens[ticker] = df["open"]
|
|
59
|
+
if "high" in df.columns:
|
|
60
|
+
highs[ticker] = df["high"]
|
|
61
|
+
if "low" in df.columns:
|
|
62
|
+
lows[ticker] = df["low"]
|
|
63
|
+
|
|
64
|
+
dt_index = pd.to_datetime(df.index)
|
|
65
|
+
all_dates = all_dates.union(dt_index)
|
|
66
|
+
|
|
67
|
+
all_dates = all_dates.sort_values()
|
|
68
|
+
|
|
69
|
+
def build_and_align(series_dict: dict, fill_method: str) -> pd.DataFrame:
|
|
70
|
+
if not series_dict:
|
|
71
|
+
return pd.DataFrame()
|
|
72
|
+
|
|
73
|
+
df = pd.DataFrame(series_dict)
|
|
74
|
+
df = df.reindex(all_dates)
|
|
75
|
+
|
|
76
|
+
if fill_method == "ffill":
|
|
77
|
+
return df.ffill()
|
|
78
|
+
elif fill_method == "zero":
|
|
79
|
+
return df.fillna(0.0)
|
|
80
|
+
return df
|
|
81
|
+
|
|
82
|
+
prices_df = build_and_align(closes, fill_method="ffill")
|
|
83
|
+
|
|
84
|
+
features = {}
|
|
85
|
+
if volumes:
|
|
86
|
+
features["volume"] = build_and_align(volumes, fill_method="zero")
|
|
87
|
+
if opens:
|
|
88
|
+
features["open"] = build_and_align(opens, fill_method="ffill")
|
|
89
|
+
if highs:
|
|
90
|
+
features["high"] = build_and_align(highs, fill_method="ffill")
|
|
91
|
+
if lows:
|
|
92
|
+
features["low"] = build_and_align(lows, fill_method="ffill")
|
|
93
|
+
|
|
94
|
+
valid_indices = prices_df.dropna(how="all").index
|
|
95
|
+
prices_df = prices_df.loc[valid_indices]
|
|
96
|
+
|
|
97
|
+
for key in features:
|
|
98
|
+
features[key] = features[key].loc[valid_indices]
|
|
99
|
+
|
|
100
|
+
return MarketData(prices=prices_df, features=features)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import datetime as dt
|
|
4
|
+
from pyalloq_data_connector.base import BaseDataClient
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class EODClient(BaseDataClient):
|
|
8
|
+
"""Adapter for EOD Historical Data (eodhd.com)."""
|
|
9
|
+
|
|
10
|
+
BASE_URL = "https://eodhd.com/api/eod"
|
|
11
|
+
|
|
12
|
+
def fetch_raw_data(
|
|
13
|
+
self, tickers: list[str], start: dt.datetime, end: dt.datetime
|
|
14
|
+
) -> dict[str, pd.DataFrame]:
|
|
15
|
+
if not self.api_key:
|
|
16
|
+
raise ValueError("EODHD requires an API key.")
|
|
17
|
+
|
|
18
|
+
start_str = start.strftime("%Y-%m-%d")
|
|
19
|
+
end_str = end.strftime("%Y-%m-%d")
|
|
20
|
+
|
|
21
|
+
raw_dict = {}
|
|
22
|
+
for ticker in tickers:
|
|
23
|
+
url = f"{self.BASE_URL}/{ticker}"
|
|
24
|
+
params = {
|
|
25
|
+
"api_token": self.api_key,
|
|
26
|
+
"fmt": "json",
|
|
27
|
+
"from": start_str,
|
|
28
|
+
"to": end_str,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
response = requests.get(url, params=params)
|
|
32
|
+
if response.status_code != 200:
|
|
33
|
+
print(
|
|
34
|
+
f"Warning: EOD returned status {response.status_code} for {ticker}"
|
|
35
|
+
)
|
|
36
|
+
continue
|
|
37
|
+
|
|
38
|
+
df = pd.DataFrame(response.json())
|
|
39
|
+
if not df.empty:
|
|
40
|
+
df["date"] = pd.to_datetime(df["date"])
|
|
41
|
+
df.set_index("date", inplace=True)
|
|
42
|
+
df["close"] = df["adjusted_close"]
|
|
43
|
+
raw_dict[ticker] = df
|
|
44
|
+
|
|
45
|
+
return raw_dict
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import datetime as dt
|
|
4
|
+
from pyalloq_data_connector.base import BaseDataClient
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FinnhubClient(BaseDataClient):
|
|
9
|
+
"""Adapter for Finnhub's stock candle endpoint."""
|
|
10
|
+
|
|
11
|
+
BASE_URL = "https://finnhub.io/api/v1/stock/candle"
|
|
12
|
+
|
|
13
|
+
def fetch_raw_data(
|
|
14
|
+
self, tickers: list[str], start: dt.datetime, end: dt.datetime
|
|
15
|
+
) -> dict[str, pd.DataFrame]:
|
|
16
|
+
if not self.api_key:
|
|
17
|
+
raise ValueError("Finnhub requires an API key.")
|
|
18
|
+
|
|
19
|
+
raw_dict = {}
|
|
20
|
+
|
|
21
|
+
start_unix = int(start.timestamp())
|
|
22
|
+
end_unix = int(end.timestamp())
|
|
23
|
+
|
|
24
|
+
for ticker in tickers:
|
|
25
|
+
params: dict[str, str | int] = {
|
|
26
|
+
"symbol": ticker,
|
|
27
|
+
"resolution": "D", # Daily bars
|
|
28
|
+
"from": start_unix,
|
|
29
|
+
"to": end_unix,
|
|
30
|
+
"token": self.api_key,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
response = requests.get(self.BASE_URL, params=params)
|
|
34
|
+
data = response.json()
|
|
35
|
+
|
|
36
|
+
if data.get("s") != "ok":
|
|
37
|
+
print(f"Warning: Finnhub returned no data or error for {ticker}")
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
df = pd.DataFrame(
|
|
41
|
+
{
|
|
42
|
+
"open": data["o"],
|
|
43
|
+
"high": data["h"],
|
|
44
|
+
"low": data["l"],
|
|
45
|
+
"close": data["c"],
|
|
46
|
+
"volume": data["v"],
|
|
47
|
+
},
|
|
48
|
+
index=pd.to_datetime(data["t"], unit="s"),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
dt_idx = pd.DatetimeIndex(df.index)
|
|
52
|
+
df.index = dt_idx.tz_localize(None).normalize()
|
|
53
|
+
raw_dict[ticker] = df
|
|
54
|
+
time.sleep(0.5)
|
|
55
|
+
|
|
56
|
+
return raw_dict
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Marker file for PEP 561
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import yfinance as yf
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import datetime as dt
|
|
4
|
+
from pyalloq_data_connector.base import BaseDataClient
|
|
5
|
+
from pyalloq_core.data import MarketData
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class YahooFinanceClient(BaseDataClient):
|
|
9
|
+
"""
|
|
10
|
+
Adapter for Yahoo Finance.
|
|
11
|
+
Does not require an API key.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
super().__init__(api_key=None)
|
|
16
|
+
|
|
17
|
+
def fetch_raw_data(
|
|
18
|
+
self, tickers: list[str], start: dt.datetime, end: dt.datetime
|
|
19
|
+
) -> dict[str, pd.DataFrame]:
|
|
20
|
+
raw_dict = {}
|
|
21
|
+
for t in tickers:
|
|
22
|
+
yt = yf.Ticker(t)
|
|
23
|
+
df = yt.history(start=start, end=end, auto_adjust=True)
|
|
24
|
+
|
|
25
|
+
if not df.empty:
|
|
26
|
+
df.index = pd.to_datetime(df.index).tz_localize(None)
|
|
27
|
+
raw_dict[t] = df
|
|
28
|
+
else:
|
|
29
|
+
print(f"Warning: No data found for {t} on Yahoo Finance.")
|
|
30
|
+
|
|
31
|
+
return raw_dict
|
|
32
|
+
|
|
33
|
+
def fetch_historical_prices(
|
|
34
|
+
self, tickers: list[str], start_date: dt.datetime, end_date: dt.datetime
|
|
35
|
+
) -> MarketData:
|
|
36
|
+
return self.get_market_data(tickers, start_date, end_date)
|