Skip to content

Commit baa0602

Browse files
Merge pull request #59 from tidy-finance/align-osap-beginning-of-month-and-scale-returns
Align OSAP download with beginning-of-month and scale returns
2 parents 9ea4dae + d5dedbd commit baa0602

4 files changed

Lines changed: 37 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
## Unreleased
44

5+
- **OSAP download aligned with beginning-of-month and scaled returns:**
6+
`download_data("Open Source Asset Pricing")` now aligns the `date`
7+
column to the beginning of the month (the dataset previously returned
8+
end-of-month dates), matching the convention used by the other
9+
download functions. All predictor columns are monthly long-short
10+
returns expressed in percent and are now divided by 100 to return
11+
plain numeric (decimal) returns.
512
- **`sorting_variable` is now optional for `factor_library`:** Calling
613
`download_data("Tidy Finance", "factor_library")` without a
714
`sorting_variable` now returns the default portfolio construction for

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ authors = [{name = "Christoph Frey", email = "christoph.frey@gmail.com"},
88
{name = "Christoph Scheuch", email = "christoph@tidy-intelligence.com"},
99
{name = "Stefan Voigt", email = "stefan.voigt@econ.ku.dk"}
1010
]
11-
version = "0.3.1.dev1"
11+
version = "0.3.1.dev2"
1212
description = "Tidy Finance Helper Functions"
1313
readme = "README.md"
1414
license = "MIT"

tests/test_download_data_osap.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def test_downloads_and_processes_all_rows():
1919
"""Test downloads and processes all rows."""
2020
raw = pd.DataFrame(
2121
{
22-
"date": ["2020-01-01", "2020-02-01"],
22+
"date": ["2020-01-31", "2020-02-29"],
2323
"LongName": [1, 2],
2424
}
2525
)
@@ -28,17 +28,20 @@ def test_downloads_and_processes_all_rows():
2828

2929
assert isinstance(result, pd.DataFrame)
3030
assert list(result.columns) == ["date", "long_name"]
31+
# Dates are aligned to the beginning of the month.
3132
assert list(result["date"]) == [
3233
pd.Timestamp("2020-01-01"),
3334
pd.Timestamp("2020-02-01"),
3435
]
36+
# Percentage returns are scaled to numeric (decimal) values.
37+
assert list(result["long_name"]) == [0.01, 0.02]
3538

3639

3740
def test_filters_rows_when_both_dates_are_supplied():
3841
"""Test filters rows when both dates are supplied."""
3942
raw = pd.DataFrame(
4043
{
41-
"date": ["2020-01-01", "2020-02-01", "2020-03-01"],
44+
"date": ["2020-01-31", "2020-02-29", "2020-03-31"],
4245
"value": [1, 2, 3],
4346
}
4447
)
@@ -49,7 +52,7 @@ def test_filters_rows_when_both_dates_are_supplied():
4952

5053
assert len(result) == 1
5154
assert result["date"].iloc[0] == pd.Timestamp("2020-02-01")
52-
assert result["value"].iloc[0] == 2
55+
assert result["value"].iloc[0] == 0.02
5356

5457

5558
def test_returns_empty_dataframe_after_download_failure():

tidyfinance/download_open_source.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,8 +1099,14 @@ def _download_data_osap(
10991099
Downloads the data from the Open Source Asset Pricing project at
11001100
https://www.openassetpricing.com/data/ from Google Sheets using a
11011101
specified sheet ID, processes the data by converting column names
1102-
to snake_case, and optionally filters the data based on a provided
1103-
date range.
1102+
to snake_case, aligning the date to the beginning of the month,
1103+
scaling the percentage long-short returns to numeric values, and
1104+
optionally filters the data based on a provided date range.
1105+
1106+
The dataset contains monthly long-short returns of the predictor
1107+
portfolios. Every column other than ``date`` is a return expressed
1108+
in percent, so all of them are divided by 100 to convert them into
1109+
plain numeric (decimal) returns.
11041110
11051111
Parameters
11061112
----------
@@ -1121,9 +1127,11 @@ def _download_data_osap(
11211127
-------
11221128
pd.DataFrame
11231129
A data frame containing the processed data. The column names
1124-
are converted to snake_case, and the data is filtered by the
1125-
specified date range if 'start_date' and 'end_date' are
1126-
provided.
1130+
are converted to snake_case, the ``date`` column is aligned to
1131+
the beginning of the month, all predictor columns (long-short
1132+
returns in percent) are divided by 100 to obtain plain numeric
1133+
(decimal) returns, and the data is filtered by the specified
1134+
date range if 'start_date' and 'end_date' are provided.
11271135
11281136
Examples
11291137
--------
@@ -1158,12 +1166,21 @@ def _download_data_osap(
11581166
return raw_data
11591167

11601168
if "date" in raw_data.columns:
1161-
raw_data["date"] = pd.to_datetime(raw_data["date"], errors="coerce")
1169+
raw_data["date"] = (
1170+
pd.to_datetime(raw_data["date"], errors="coerce")
1171+
.dt.to_period("M")
1172+
.dt.start_time
1173+
)
11621174

11631175
raw_data.columns = [
11641176
_transfrom_to_snake_case(col) for col in raw_data.columns
11651177
]
11661178

1179+
# All columns except the date are long-short returns in percent, so
1180+
# scale them to plain numeric (decimal) returns.
1181+
return_columns = [col for col in raw_data.columns if col != "date"]
1182+
raw_data[return_columns] = raw_data[return_columns] / 100
1183+
11671184
if start_date and end_date:
11681185
raw_data = raw_data.query("@start_date <= date <= @end_date")
11691186

0 commit comments

Comments
 (0)