PriceSeries

Defined in fynance.core

class PriceSeries(values, index=None, name=None, freq=None)[source]

Bases: object

Thin, numpy-backed financial time-series.

Wraps a 1-D array of values together with a temporal index and light metadata (name, freq). It composes a numpy array rather than subclassing it, which keeps numpy interop (np.asarray(ps) works) without the fragility of ndarray subclassing.

Parameters:
valuesarray-like

1-D sequence of values (coerced to float64).

indexarray-like, optional

Temporal index of the same length. Defaults to 0..n-1 (int64).

namestr, optional

Series name.

freqstr, optional

Sampling frequency tag (e.g. "1d"), purely informational.

Attributes:
valuesnumpy.ndarray

Read-only float64 1-D array of values.

indexnumpy.ndarray

1-D array aligned with values.

namestr or None

Series name.

freqstr or None

Frequency tag.

Examples

>>> ps = PriceSeries([100., 101., 99.], name="px")
>>> len(ps)
3
>>> ps[1]
101.0
apply(func)[source]

Apply an element-wise function to the values.

On an empty series this short-circuits to an empty result (np.vectorize cannot infer an output dtype from zero inputs).

copy()[source]

Return a copy of this series.

cumulative()[source]

Equity curve of this return series: cumprod(1 + r).

drop_na()[source]

Drop NaN observations (and their index entries).

fillna(value=0.0)[source]

Replace NaN observations by a constant value.

classmethod from_numpy(values, index=None, name=None, freq=None)[source]

Build a PriceSeries from numpy arrays.

classmethod from_pandas(obj, freq=None)[source]

Build a PriceSeries from a duck-typed pandas-like object.

Accepts anything exposing .to_numpy() or .values – a pandas.Series, a single-column pandas.DataFrame, or a minimal look-alike. No pandas import happens in this method: it is pure duck typing, so any object satisfying the protocol works (this is what lets the test suite exercise it with a pandas-free shim).

Parameters:
objAny

Duck-typed pandas-like object (Series or single-column DataFrame).

freqstr, optional

Frequency tag carried onto the series.

Returns:
PriceSeries
Raises:
TypeError

If obj exposes neither .to_numpy() nor .values.

ValueError

If the extracted array is 2-D with more than one column (an ambiguous, multi-column DataFrame).

Notes

Unlike from_polars (which drops the index – polars frames rarely carry a meaningful one), PriceSeries is built here with its index attribute populated from obj.index whenever that attribute is present (e.g. a pandas Series’s DatetimeIndex), converted through numpy.asarray. It is only left at the default 0..n-1 range when obj exposes no .index at all, as with the minimal duck-typed shim used in the tests.

Examples

>>> import pandas as pd
>>> s = pd.Series([100., 101., 99.], name="px")
>>> PriceSeries.from_pandas(s).values
array([100., 101.,  99.])
classmethod from_polars(data, value_col=None, index_col=None, freq=None)[source]

Build a PriceSeries from a polars Series or DataFrame.

A polars.Series maps directly to the values. For a polars.DataFrame, value_col selects the value column (defaults to the single non-index numeric column) and index_col (or the first temporal column) becomes the index.

pipe(func, *args, **kwargs)[source]

Apply a free function to the values, re-wrapping when possible.

Delegates to the rich transform library without bloating this class: ps.pipe(rsi, window=14) calls rsi(ps.values, window=14). If the result is a 1-D array of the same length it is wrapped back into a PriceSeries (same index); otherwise the raw result is returned.

pnl(positions)[source]

Strategy returns of a position book on this return series.

Causal: the position is shifted forward one step, so the position decided at t-1 earns the return at t (pnl_t = position_{t-1} * r_t). The first observation is NaN (no prior position).

Parameters:
positionsarray-like

Position series aligned with this return series.

Returns:
PriceSeries

Strategy return series.

to_numpy(copy=True)[source]

Return the values as a numpy array (copied by default).

to_pandas()[source]

Return this series as a pandas.Series (lazy import).

Raises:
ImportError

If pandas is not installed, with a clear, actionable message.

Examples

>>> PriceSeries([100., 101., 99.], name="px").to_pandas()
0    100.0
1    101.0
2     99.0
Name: px, dtype: float64
to_polars()[source]

Return this series as a polars.Series (lazy import).

Raises:
ImportError

If polars is not installed, with a clear, actionable message. (polars is a hard dependency of fynance, so this branch is not normally reachable in this package’s own environment; it exists so the method degrades gracefully wherever it is vendored or reused.)

Examples

>>> PriceSeries([1., 2., 3.], name="px").to_polars().to_list()
[1.0, 2.0, 3.0]
to_prices(base=1.0, kind='pct')[source]

Reconstruct a price path from this return series (inverse of to_returns).

Parameters:
basefloat

Initial price prepended to the path.

kind{“pct”, “log”, “raw”}

Must match the convention the returns were computed with.

Returns:
PriceSeries

Price path of length len(self) + 1. The index is reset to the default 0..n range: the reconstructed path has one extra leading point (the base price) with no corresponding timestamp in the original return index, so a meaningful index cannot be carried through. Re-attach an index explicitly if needed.

to_returns(kind='pct', dropna=True)[source]

Convert a price path to a return series.

Parameters:
kind{“pct”, “log”, “raw”}

pct -> p_t / p_{t-1} - 1; log -> ln(p_t / p_{t-1}); raw -> p_t - p_{t-1}.

dropnabool

Drop the (undefined) first observation. If False, a leading NaN is kept so the length is preserved.

Returns:
PriceSeries

The return series (strictly causal: r_t uses only p_t and p_{t-1}).

Raises:
ValueError

For kind="pct" or "log" when any price is non-positive (<= 0). Both conventions divide by the previous price (and log takes its logarithm), so a zero or negative price would otherwise yield silent inf/nan with only a bare RuntimeWarning. Use kind="raw" for series that legitimately cross or touch zero.

Examples

>>> PriceSeries([100., 110., 99.]).to_returns("pct").values
array([ 0.1, -0.1])
to_torch(dtype=None, device=None)[source]

Return the values as a torch.Tensor (lazy torch import).