tessa.price.price

Retrieve price information.

  1"""Retrieve price information."""
  2
  3from __future__ import annotations
  4from functools import lru_cache, wraps
  5from typing import Union, TYPE_CHECKING
  6import pandas as pd
  7from . import PriceHistory, PricePoint
  8from .. import sources
  9
 10if TYPE_CHECKING:
 11    from .. import SourceType
 12
 13
 14def custom_cache_wrapper(func):
 15    """To preserve the function's signature _and_ give access to `cache_clear` etc."""
 16    cached_func = lru_cache(maxsize=None)(func)
 17    wrapped_func = wraps(func)(cached_func)
 18    return wrapped_func
 19
 20
 21@custom_cache_wrapper
 22def price_history(
 23    query: str,
 24    source: SourceType = "yahoo",
 25    currency_preference: str = "USD",
 26) -> PriceHistory:
 27    """Get price history and return `PriceHistory`, i.e., a tuple of a dataframe with
 28    the price history and the effective currency. Note that the effective currency
 29    returned might differ from the currency_preference.
 30
 31    - `query`: A query string that makes sense in combination with the source. E.g.,
 32      "BTC-USD" for "yahoo" or "bitcoin" for "coingecko".
 33    - `source`: The source to query. Defaults to "yahoo".
 34    - `currency_preference`: The currency the prices should be returned in; defaults
 35      to "USD". The effective currency might differ and will be returned in the second
 36      return value.
 37    """
 38    src = sources.get_source(source)
 39    src.rate_limiter.rate_limit()
 40    df, effective_currency = src.get_price_history_bruteforcefully(
 41        query, currency_preference
 42    )
 43    return PriceHistory(df.copy(), effective_currency.upper())
 44    # (Returning a copy of the dataframe so the cached original is preserved even if it
 45    # gets modified by the caller.)
 46
 47
 48def price_point(
 49    query: str,
 50    when: Union[str, pd.Timestamp],
 51    source: SourceType = "yahoo",
 52    currency_preference: str = "USD",
 53    max_date_deviation_days: Union[int, None] = 10,
 54) -> PricePoint:
 55    """Return the price at a given point in time given by `when`. Look for the closest
 56    point in time if the exact point in time is not found. Returns a `PricePoint`, i.e.,
 57    a tuple of the price, the effective timestamp of the price, and the currency.
 58
 59    Raises a `ValueError` if the found date is more than `max_date_deviation_days` from
 60    `when`. Use `None` to disable this check.
 61
 62    Arguments other than `when` are the same as with `price_history`.
 63
 64    Example call:
 65    ```
 66    price_point("AAPL", "2020-01-01")
 67    ```
 68    """
 69    df, currency = price_history(query, source, currency_preference)
 70
 71    when = pd.Timestamp(when)
 72    if df.index.tz is not None:  # Ensure when matches the timezone of df.index
 73        when = when.tz_localize("UTC") if when.tz is None else when.tz_convert("UTC")
 74
 75    nearest_index = df.index.get_indexer([when], method="nearest")[0]
 76    found_date = df.index[nearest_index]
 77
 78    if (
 79        max_date_deviation_days is not None
 80        and abs((found_date - when).days) > max_date_deviation_days
 81    ):
 82        raise ValueError(
 83            f"Found date {found_date} is more than {max_date_deviation_days} days away "
 84            f"from requested date {when}"
 85        )
 86
 87    price = df.iloc[nearest_index]
 88    return PricePoint(when=found_date, price=float(price.iloc[0]), currency=currency)
 89
 90
 91def price_point_strict(
 92    query: str,
 93    when: str,
 94    source: SourceType = "yahoo",
 95    currency_preference: str = "USD",
 96) -> PricePoint:
 97    """Same as `price_point` but will raise a `KeyError` if no price is found. This
 98    function is offered for backwards compatibility and is largely obsolete with the
 99    introduction of `max_date_deviation_days` in `price_point`.
100    """
101    try:
102        return price_point(
103            query, when, source, currency_preference, max_date_deviation_days=0
104        )
105    except ValueError as e:
106        raise KeyError(f"No price found for {query} at {when}") from e
107
108
109def price_latest(
110    query: str,
111    source: SourceType = "yahoo",
112    currency_preference: str = "USD",
113) -> PricePoint:
114    """Same as `price_point` but will return the latest price."""
115    df, currency = price_history(query, source, currency_preference)
116    return PricePoint(
117        when=df.iloc[-1].name, price=float(df.iloc[-1]["close"]), currency=currency
118    )
def custom_cache_wrapper(func):
15def custom_cache_wrapper(func):
16    """To preserve the function's signature _and_ give access to `cache_clear` etc."""
17    cached_func = lru_cache(maxsize=None)(func)
18    wrapped_func = wraps(func)(cached_func)
19    return wrapped_func

To preserve the function's signature _and_ give access to cache_clear etc.

@custom_cache_wrapper
def price_history( query: str, source: Literal['yahoo', 'coingecko'] = 'yahoo', currency_preference: str = 'USD') -> tessa.price.types.PriceHistory:
22@custom_cache_wrapper
23def price_history(
24    query: str,
25    source: SourceType = "yahoo",
26    currency_preference: str = "USD",
27) -> PriceHistory:
28    """Get price history and return `PriceHistory`, i.e., a tuple of a dataframe with
29    the price history and the effective currency. Note that the effective currency
30    returned might differ from the currency_preference.
31
32    - `query`: A query string that makes sense in combination with the source. E.g.,
33      "BTC-USD" for "yahoo" or "bitcoin" for "coingecko".
34    - `source`: The source to query. Defaults to "yahoo".
35    - `currency_preference`: The currency the prices should be returned in; defaults
36      to "USD". The effective currency might differ and will be returned in the second
37      return value.
38    """
39    src = sources.get_source(source)
40    src.rate_limiter.rate_limit()
41    df, effective_currency = src.get_price_history_bruteforcefully(
42        query, currency_preference
43    )
44    return PriceHistory(df.copy(), effective_currency.upper())
45    # (Returning a copy of the dataframe so the cached original is preserved even if it
46    # gets modified by the caller.)

Get price history and return PriceHistory, i.e., a tuple of a dataframe with the price history and the effective currency. Note that the effective currency returned might differ from the currency_preference.

  • query: A query string that makes sense in combination with the source. E.g., "BTC-USD" for "yahoo" or "bitcoin" for "coingecko".
  • source: The source to query. Defaults to "yahoo".
  • currency_preference: The currency the prices should be returned in; defaults to "USD". The effective currency might differ and will be returned in the second return value.
def price_point( query: str, when: Union[str, pandas._libs.tslibs.timestamps.Timestamp], source: Literal['yahoo', 'coingecko'] = 'yahoo', currency_preference: str = 'USD', max_date_deviation_days: Optional[int] = 10) -> tessa.price.types.PricePoint:
49def price_point(
50    query: str,
51    when: Union[str, pd.Timestamp],
52    source: SourceType = "yahoo",
53    currency_preference: str = "USD",
54    max_date_deviation_days: Union[int, None] = 10,
55) -> PricePoint:
56    """Return the price at a given point in time given by `when`. Look for the closest
57    point in time if the exact point in time is not found. Returns a `PricePoint`, i.e.,
58    a tuple of the price, the effective timestamp of the price, and the currency.
59
60    Raises a `ValueError` if the found date is more than `max_date_deviation_days` from
61    `when`. Use `None` to disable this check.
62
63    Arguments other than `when` are the same as with `price_history`.
64
65    Example call:
66    ```
67    price_point("AAPL", "2020-01-01")
68    ```
69    """
70    df, currency = price_history(query, source, currency_preference)
71
72    when = pd.Timestamp(when)
73    if df.index.tz is not None:  # Ensure when matches the timezone of df.index
74        when = when.tz_localize("UTC") if when.tz is None else when.tz_convert("UTC")
75
76    nearest_index = df.index.get_indexer([when], method="nearest")[0]
77    found_date = df.index[nearest_index]
78
79    if (
80        max_date_deviation_days is not None
81        and abs((found_date - when).days) > max_date_deviation_days
82    ):
83        raise ValueError(
84            f"Found date {found_date} is more than {max_date_deviation_days} days away "
85            f"from requested date {when}"
86        )
87
88    price = df.iloc[nearest_index]
89    return PricePoint(when=found_date, price=float(price.iloc[0]), currency=currency)

Return the price at a given point in time given by when. Look for the closest point in time if the exact point in time is not found. Returns a PricePoint, i.e., a tuple of the price, the effective timestamp of the price, and the currency.

Raises a ValueError if the found date is more than max_date_deviation_days from when. Use None to disable this check.

Arguments other than when are the same as with price_history.

Example call:

price_point("AAPL", "2020-01-01")
def price_point_strict( query: str, when: str, source: Literal['yahoo', 'coingecko'] = 'yahoo', currency_preference: str = 'USD') -> tessa.price.types.PricePoint:
 92def price_point_strict(
 93    query: str,
 94    when: str,
 95    source: SourceType = "yahoo",
 96    currency_preference: str = "USD",
 97) -> PricePoint:
 98    """Same as `price_point` but will raise a `KeyError` if no price is found. This
 99    function is offered for backwards compatibility and is largely obsolete with the
100    introduction of `max_date_deviation_days` in `price_point`.
101    """
102    try:
103        return price_point(
104            query, when, source, currency_preference, max_date_deviation_days=0
105        )
106    except ValueError as e:
107        raise KeyError(f"No price found for {query} at {when}") from e

Same as price_point but will raise a KeyError if no price is found. This function is offered for backwards compatibility and is largely obsolete with the introduction of max_date_deviation_days in price_point.

def price_latest( query: str, source: Literal['yahoo', 'coingecko'] = 'yahoo', currency_preference: str = 'USD') -> tessa.price.types.PricePoint:
110def price_latest(
111    query: str,
112    source: SourceType = "yahoo",
113    currency_preference: str = "USD",
114) -> PricePoint:
115    """Same as `price_point` but will return the latest price."""
116    df, currency = price_history(query, source, currency_preference)
117    return PricePoint(
118        when=df.iloc[-1].name, price=float(df.iloc[-1]["close"]), currency=currency
119    )

Same as price_point but will return the latest price.