-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add variants that retrieve price information in real time
- Add `mod_investpy_pme` module with `investpy_pme` and `investpy_verbose_pme` using `investpy` module, which uses Investing.com as the data source. - Also, fix a couple of issues with hypothesis testing and making sure the input dates are sorted.
- Loading branch information
Showing
7 changed files
with
203 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
__version__ = '0.1.3' | ||
__version__ = '0.2.0' | ||
from .pme import verbose_pme, pme, verbose_xpme, xpme | ||
from .mod_investpy_pme import investpy_verbose_pme, investpy_pme |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
"""Calculate PME and get the prices from Investing.com via the `investpy` module. | ||
Important: The Investing API has rate limiting measures in place and will block you if | ||
you hit the API too often. You will notice by getting 429 errors (or maybe | ||
also/alternatively 503). Wait roughly 2 seconds between each consecutive call to the API | ||
via the functions in this module. | ||
Args: | ||
- pme_type: One of "stock", "etf", "fund", "crypto", "bond", "index", "certificate". | ||
Defaults to "stock". | ||
- pme_ticker: The ticker symbol/name. | ||
- pme_country: The ticker's country of residence. Defaults to "united states". | ||
Refer to the `pme` module to understand other arguments and what the functions return. | ||
""" | ||
|
||
from typing import List, Tuple | ||
from datetime import date | ||
import pandas as pd | ||
import investpy | ||
from .pme import verbose_xpme | ||
|
||
|
||
def get_historical_data(ticker: str, type: str, **kwargs) -> pd.DataFrame: | ||
"""Small wrapper to make the investpy interface accessible in a more unified fashion.""" | ||
kwargs[type] = ticker | ||
if type == "crypto" and "country" in kwargs: | ||
del kwargs["country"] | ||
return getattr(investpy, "get_" + type + "_historical_data")(**kwargs) | ||
|
||
|
||
def investpy_verbose_pme( | ||
dates: List[date], | ||
cashflows: List[float], | ||
prices: List[float], | ||
pme_ticker: str, | ||
pme_type: str = "stock", | ||
pme_country: str = "united states", | ||
) -> Tuple[float, float, pd.DataFrame]: | ||
"""Calculate PME return vebose information, retrieving PME price information from | ||
Investing.com in real time. | ||
""" | ||
dates_as_str = [x.strftime("%d/%m/%Y") for x in sorted(dates)] | ||
pmedf = get_historical_data( | ||
pme_ticker, | ||
pme_type, | ||
country=pme_country, | ||
from_date=dates_as_str[0], | ||
to_date=dates_as_str[-1], | ||
) | ||
# Pick the nearest price if there is no price for an exact date: | ||
pme_prices = [ | ||
pmedf.iloc[pmedf.index.get_indexer([x], method="nearest")[0]]["Close"] | ||
for x in dates_as_str | ||
] | ||
return verbose_xpme(dates, cashflows, prices, pme_prices) | ||
|
||
|
||
def investpy_pme( | ||
dates: List[date], | ||
cashflows: List[float], | ||
prices: List[float], | ||
pme_ticker: str, | ||
pme_type: str = "stock", | ||
pme_country: str = "united states", | ||
) -> Tuple[float, float, pd.DataFrame]: | ||
"""Calculate PME and return the PME IRR only, retrieving PME price information from | ||
Investing.com in real time. | ||
""" | ||
return investpy_verbose_pme( | ||
dates, cashflows, prices, pme_ticker, pme_type, pme_country | ||
)[0] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import pytest | ||
from datetime import date | ||
import pandas as pd | ||
from pypme.mod_investpy_pme import investpy_pme, investpy_verbose_pme | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"dates, cashflows, prices, pme_timestamps, pme_prices, target_pme_irr, target_asset_irr", | ||
[ | ||
( | ||
[date(2012, 1, 1), date(2013, 1, 1)], | ||
[-100], | ||
[1, 1], | ||
["2012-01-01"], | ||
[20], | ||
0, | ||
# B/c the function will search for the nearest date which is always the same | ||
# one b/c there is only one and therefore produce a PME IRR of 0 | ||
0, | ||
), | ||
( | ||
[date(2012, 1, 1), date(2013, 1, 1)], | ||
[-100], | ||
[1, 1], | ||
["2012-01-01", "2012-01-02"], | ||
[20, 40], | ||
99.62, | ||
# In this case, the "nearest" option in `investpy_verbose_pme`'s call to | ||
# `get_indexer` finds the entry at 2012-01-02. Even though it's far away | ||
# from 2013-01-01, it's still the closest. | ||
0, | ||
), | ||
], | ||
) | ||
def test_investpy_pme( | ||
mocker, | ||
dates, | ||
cashflows, | ||
prices, | ||
pme_timestamps, | ||
pme_prices, | ||
target_pme_irr, | ||
target_asset_irr, | ||
): | ||
"""Test both the verbose and non-verbose variant at the same to keep things simple. | ||
Note that this test does _not_ hit the network / investing API since the relevant | ||
function gets mocked. | ||
""" | ||
mocker.patch( | ||
"pypme.mod_investpy_pme.get_historical_data", | ||
return_value=pd.DataFrame( | ||
{"Close": {pd.Timestamp(x): y for x, y in zip(pme_timestamps, pme_prices)}} | ||
), | ||
) | ||
pme_irr, asset_irr, df = investpy_verbose_pme( | ||
dates=dates, | ||
cashflows=cashflows, | ||
prices=prices, | ||
pme_ticker="dummy", | ||
) | ||
assert round(pme_irr * 100.0, 2) == round(target_pme_irr, 2) | ||
assert round(asset_irr * 100.0, 2) == round(target_asset_irr, 2) | ||
assert isinstance(df, pd.DataFrame) | ||
|
||
pme_irr = investpy_pme( | ||
dates=dates, | ||
cashflows=cashflows, | ||
prices=prices, | ||
pme_ticker="dummy", | ||
) | ||
assert round(pme_irr * 100.0, 2) == round(target_pme_irr, 2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters