Skip to content

Commit 590b1ef

Browse files
authored
added stock splits and tests (#24)
1 parent 50ac35b commit 590b1ef

File tree

3 files changed

+184
-1
lines changed

3 files changed

+184
-1
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "fmp-py"
3-
version = "0.0.15.0"
3+
version = "0.0.16.0"
44
description = "Python package for Financial Modeling Prep API"
55
authors = ["TexasCoding <[email protected]>"]
66
readme = "README.md"

src/fmp_py/fmp_splits.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import pandas as pd
2+
import pendulum
3+
from fmp_py.fmp_base import FmpBase
4+
import os
5+
from dotenv import load_dotenv
6+
7+
load_dotenv()
8+
9+
"""
10+
Retrieves Stock Spilts Data from Financial Modeling Prep API
11+
References:
12+
- https://site.financialmodelingprep.com/developer/docs#splits
13+
14+
def stock_splits_calendar(self, from_date: str, to_date: str) -> pd.DataFrame:
15+
Reference: https://site.financialmodelingprep.com/developer/docs#splits-calendar-splits
16+
17+
def stock_splits_historical(self, symbol: str) -> pd.DataFrame:
18+
Reference: https://site.financialmodelingprep.com/developer/docs#splits-historical-splits
19+
"""
20+
21+
22+
class FmpSplits(FmpBase):
23+
def __init__(self, api_key: str = os.getenv("FMP_API_KEY")) -> None:
24+
super().__init__(api_key)
25+
26+
##########################################
27+
# Stock Splits Histrorical
28+
##########################################
29+
def stock_splits_historical(self, symbol: str) -> pd.DataFrame:
30+
"""
31+
Retrieves historical stock splits data for a given symbol.
32+
Args:
33+
symbol (str): The stock symbol.
34+
Returns:
35+
pd.DataFrame: A DataFrame containing the historical stock splits data.
36+
"""
37+
38+
url = f"v3/historical-price-full/stock_split/{symbol}"
39+
response = self.get_request(url)["historical"]
40+
41+
if not response:
42+
raise ValueError(
43+
f"Error fetching stock splits historical data for {symbol}"
44+
)
45+
46+
data_df = (
47+
pd.DataFrame(response)
48+
.fillna(0)
49+
.rename(
50+
columns={
51+
"date": "date",
52+
"label": "label",
53+
"numerator": "numerator",
54+
"denominator": "denominator",
55+
}
56+
)
57+
.astype(
58+
{
59+
"date": "datetime64[ns]",
60+
"label": "str",
61+
"numerator": "int",
62+
"denominator": "int",
63+
}
64+
)
65+
.sort_values(by="date", ascending=True)
66+
.reset_index(drop=True)
67+
)
68+
data_df["symbol"] = symbol
69+
70+
return data_df
71+
72+
########################################
73+
# Stock Splits Calendar
74+
########################################
75+
def stock_splits_calendar(self, from_date: str, to_date: str) -> pd.DataFrame:
76+
"""
77+
Retrieves the stock splits calendar for a given date range.
78+
Args:
79+
from_date (str): The start date of the date range in "YYYY-MM-DD" format.
80+
to_date (str): The end date of the date range in "YYYY-MM-DD" format.
81+
Returns:
82+
pd.DataFrame: A DataFrame containing the stock splits calendar data with the following columns:
83+
- date: The date of the stock split.
84+
- label: The label of the stock split.
85+
- symbol: The symbol of the stock.
86+
- numerator: The numerator of the stock split ratio.
87+
- denominator: The denominator of the stock split ratio.
88+
Raises:
89+
ValueError: If from_date is greater than to_date or if no data is found for the given date range.
90+
"""
91+
92+
from_date = pendulum.parse(from_date).format("YYYY-MM-DD")
93+
to_date = pendulum.parse(to_date).format("YYYY-MM-DD")
94+
if from_date > to_date:
95+
raise ValueError("from_date must be less than or equal to to_date")
96+
97+
url = "v3/stock_split_calendar"
98+
params = {"from": from_date, "to": to_date}
99+
response = self.get_request(url=url, params=params)
100+
101+
if not response:
102+
raise ValueError("No data found for the given date range")
103+
104+
data_df = (
105+
pd.DataFrame(response)
106+
.fillna(0)
107+
.rename(
108+
columns={
109+
"date": "date",
110+
"label": "label",
111+
"symbol": "symbol",
112+
"numerator": "numerator",
113+
"denominator": "denominator",
114+
}
115+
)
116+
.astype(
117+
{
118+
"date": "datetime64[ns]",
119+
"label": "str",
120+
"symbol": "str",
121+
"numerator": "int",
122+
"denominator": "int",
123+
}
124+
)
125+
.sort_values(by="date", ascending=True)
126+
.reset_index(drop=True)
127+
)
128+
129+
return data_df

tests/test_fmp_splits.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import numpy as np
2+
import pandas as pd
3+
import pytest
4+
5+
from fmp_py.fmp_splits import FmpSplits
6+
7+
8+
@pytest.fixture
9+
def fmp_splits():
10+
return FmpSplits()
11+
12+
13+
def test_fmp_splits_init(fmp_splits):
14+
assert isinstance(fmp_splits, FmpSplits)
15+
16+
17+
def test_fmp_splits_stock_splits_calendar(fmp_splits):
18+
result = fmp_splits.stock_splits_calendar("2023-01-01", "2023-01-31")
19+
assert isinstance(result, pd.DataFrame)
20+
assert "date" in result.columns
21+
assert "label" in result.columns
22+
assert "symbol" in result.columns
23+
assert "numerator" in result.columns
24+
assert "denominator" in result.columns
25+
assert isinstance(result["date"].iloc[0], pd.Timestamp)
26+
assert isinstance(result["label"].iloc[0], str)
27+
assert isinstance(result["symbol"].iloc[0], str)
28+
assert isinstance(result["numerator"].iloc[0], np.int64)
29+
assert isinstance(result["denominator"].iloc[0], np.int64)
30+
31+
32+
def test_fmp_splits_stock_splits_calendar_invalid_date_range(fmp_splits):
33+
with pytest.raises(ValueError):
34+
fmp_splits.stock_splits_calendar("2023-01-31", "2023-01-01")
35+
36+
37+
def test_fmp_splits_stock_splits_historical(fmp_splits):
38+
result = fmp_splits.stock_splits_historical("AAPL")
39+
assert isinstance(result, pd.DataFrame)
40+
assert "date" in result.columns
41+
assert "label" in result.columns
42+
assert "symbol" in result.columns
43+
assert "numerator" in result.columns
44+
assert "denominator" in result.columns
45+
assert isinstance(result["date"].iloc[0], pd.Timestamp)
46+
assert isinstance(result["label"].iloc[0], str)
47+
assert isinstance(result["symbol"].iloc[0], str)
48+
assert isinstance(result["numerator"].iloc[0], np.int64)
49+
assert isinstance(result["denominator"].iloc[0], np.int64)
50+
51+
52+
def test_fmp_splits_stock_splits_historical_invalid_symbol(fmp_splits):
53+
with pytest.raises(ValueError):
54+
fmp_splits.stock_splits_historical("INVALID_SYMBOL")

0 commit comments

Comments
 (0)