扩展指标
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
"""
|
||||
ferro_ta.crypto — Crypto and 24/7 market helpers.
|
||||
=================================================
|
||||
|
||||
Helpers designed for continuous (24/7) markets such as cryptocurrency or FX.
|
||||
|
||||
Functions
|
||||
---------
|
||||
funding_pnl(position_size, funding_rate)
|
||||
Compute the cumulative PnL from periodic funding rate payments.
|
||||
|
||||
continuous_bar_labels(n_bars, period_bars)
|
||||
Assign integer period labels to bars without calendar-based sessions.
|
||||
|
||||
session_boundaries(timestamps_ns)
|
||||
Return bar indices at the start of each UTC-day session boundary.
|
||||
|
||||
resample_continuous(ohlcv, period_bars)
|
||||
Resample a continuous OHLCV series by grouping every *period_bars* input
|
||||
bars into one output bar (no session filtering).
|
||||
|
||||
Rust backend
|
||||
------------
|
||||
ferro_ta._ferro_ta.funding_cumulative_pnl
|
||||
ferro_ta._ferro_ta.continuous_bar_labels
|
||||
ferro_ta._ferro_ta.mark_session_boundaries
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Union
|
||||
|
||||
import numpy as np
|
||||
from numpy.typing import ArrayLike, NDArray
|
||||
|
||||
from ferro_ta._ferro_ta import (
|
||||
continuous_bar_labels as _rust_continuous_bar_labels,
|
||||
)
|
||||
from ferro_ta._ferro_ta import (
|
||||
funding_cumulative_pnl as _rust_funding_cumulative_pnl,
|
||||
)
|
||||
from ferro_ta._ferro_ta import (
|
||||
mark_session_boundaries as _rust_mark_session_boundaries,
|
||||
)
|
||||
from ferro_ta._ferro_ta import (
|
||||
ohlcv_agg as _rust_ohlcv_agg,
|
||||
)
|
||||
from ferro_ta._utils import _to_f64
|
||||
|
||||
__all__ = [
|
||||
"funding_pnl",
|
||||
"continuous_bar_labels",
|
||||
"session_boundaries",
|
||||
"resample_continuous",
|
||||
]
|
||||
|
||||
# type alias
|
||||
OHLCVTuple = tuple[
|
||||
NDArray[np.float64],
|
||||
NDArray[np.float64],
|
||||
NDArray[np.float64],
|
||||
NDArray[np.float64],
|
||||
NDArray[np.float64],
|
||||
]
|
||||
|
||||
|
||||
def funding_pnl(
|
||||
position_size: ArrayLike,
|
||||
funding_rate: ArrayLike,
|
||||
) -> NDArray[np.float64]:
|
||||
"""Compute cumulative PnL from periodic funding rate payments.
|
||||
|
||||
Crypto perpetual contracts charge a periodic funding rate to position
|
||||
holders. A long position pays when the funding rate is positive; a short
|
||||
position receives.
|
||||
|
||||
PnL at period *i* = ``-position_size[i] * funding_rate[i]``
|
||||
Returned array is the cumulative sum of those per-period PnLs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
position_size : array-like — signed position size per funding period.
|
||||
Positive = long, negative = short.
|
||||
funding_rate : array-like — periodic funding rate in decimal notation
|
||||
(e.g. 0.0001 = 0.01%). Must have the same length as *position_size*.
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.ndarray of float64 — cumulative funding PnL.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import numpy as np
|
||||
>>> from ferro_ta.analysis.crypto import funding_pnl
|
||||
>>> pos = np.ones(5) # long 1 contract
|
||||
>>> rate = np.array([0.0001, 0.0002, -0.0001, 0.0001, 0.0001])
|
||||
>>> pnl = funding_pnl(pos, rate)
|
||||
>>> pnl.round(6)
|
||||
array([-0.0001, -0.0003, 0. , -0.0001, -0.0002])
|
||||
"""
|
||||
return np.asarray(
|
||||
_rust_funding_cumulative_pnl(_to_f64(position_size), _to_f64(funding_rate)),
|
||||
dtype=np.float64,
|
||||
)
|
||||
|
||||
|
||||
def continuous_bar_labels(
|
||||
n_bars: int,
|
||||
period_bars: int,
|
||||
) -> NDArray[np.int64]:
|
||||
"""Assign sequential integer labels to bars in equal-size buckets.
|
||||
|
||||
Useful for grouping continuous data (no session gaps) into periods without
|
||||
relying on calendar logic. Bars 0…(period_bars-1) get label 0,
|
||||
bars period_bars…(2·period_bars-1) get label 1, etc.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n_bars : int — total number of bars
|
||||
period_bars : int — number of bars per period (e.g. 24 for hourly → daily)
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.ndarray of int64 — period label per bar.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from ferro_ta.analysis.crypto import continuous_bar_labels
|
||||
>>> continuous_bar_labels(10, 3)
|
||||
array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3])
|
||||
"""
|
||||
return np.asarray(
|
||||
_rust_continuous_bar_labels(int(n_bars), int(period_bars)),
|
||||
dtype=np.int64,
|
||||
)
|
||||
|
||||
|
||||
def session_boundaries(
|
||||
timestamps_ns: ArrayLike,
|
||||
) -> NDArray[np.int64]:
|
||||
"""Return bar indices at the start of each UTC-day boundary.
|
||||
|
||||
Intended for 24/7 data where no exchange session gaps exist. Useful for
|
||||
building daily OHLCV bars from intraday continuous data.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
timestamps_ns : array-like of int64 — UTC timestamps in nanoseconds
|
||||
(e.g. ``pandas.DatetimeIndex.astype('int64')``).
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.ndarray of int64 — indices of the first bar in each UTC day
|
||||
(always includes index 0).
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import numpy as np
|
||||
>>> from ferro_ta.analysis.crypto import session_boundaries
|
||||
>>> # Two UTC days of hourly bars: day 0 = bars 0-23, day 1 = bars 24-47
|
||||
>>> base_ns = np.int64(1_700_000_000_000_000_000) # some UTC timestamp
|
||||
>>> ns_per_hour = np.int64(3_600_000_000_000)
|
||||
>>> ts = base_ns + np.arange(48, dtype=np.int64) * ns_per_hour
|
||||
>>> bounds = session_boundaries(ts)
|
||||
"""
|
||||
ts = np.asarray(timestamps_ns, dtype=np.int64)
|
||||
return np.asarray(
|
||||
_rust_mark_session_boundaries(ts),
|
||||
dtype=np.int64,
|
||||
)
|
||||
|
||||
|
||||
def resample_continuous(
|
||||
ohlcv: Union[
|
||||
tuple[ArrayLike, ArrayLike, ArrayLike, ArrayLike, ArrayLike],
|
||||
object, # pandas.DataFrame
|
||||
],
|
||||
period_bars: int,
|
||||
) -> OHLCVTuple:
|
||||
"""Resample a continuous OHLCV series by grouping *period_bars* input bars.
|
||||
|
||||
Unlike time-based resampling, this function requires no calendar or
|
||||
session information. Every *period_bars* consecutive input bars are
|
||||
aggregated into one output bar. Ideal for 24/7 crypto data.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ohlcv : tuple ``(open, high, low, close, volume)`` of array-like,
|
||||
**or** a ``pandas.DataFrame`` with columns ``open/high/low/close/volume``
|
||||
(case-insensitive).
|
||||
period_bars : int — number of input bars per output bar (must be >= 1).
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple ``(open, high, low, close, volume)`` of numpy.ndarray — resampled bars.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The last output bar may aggregate fewer than *period_bars* input bars if
|
||||
``len(close) % period_bars != 0``.
|
||||
"""
|
||||
try:
|
||||
import pandas as pd
|
||||
|
||||
if isinstance(ohlcv, pd.DataFrame):
|
||||
cols = {c.lower(): c for c in ohlcv.columns} # type: ignore[union-attr]
|
||||
o = _to_f64(ohlcv[cols["open"]].values) # type: ignore[index]
|
||||
h = _to_f64(ohlcv[cols["high"]].values) # type: ignore[index]
|
||||
lo = _to_f64(ohlcv[cols["low"]].values) # type: ignore[index]
|
||||
c = _to_f64(ohlcv[cols["close"]].values) # type: ignore[index]
|
||||
v = _to_f64(ohlcv[cols["volume"]].values) # type: ignore[index]
|
||||
else:
|
||||
o, h, lo, c, v = [_to_f64(x) for x in ohlcv] # type: ignore[union-attr]
|
||||
except ImportError:
|
||||
o, h, lo, c, v = [_to_f64(x) for x in ohlcv] # type: ignore[union-attr]
|
||||
|
||||
n = len(c)
|
||||
if period_bars < 1:
|
||||
raise ValueError("period_bars must be >= 1")
|
||||
# Build bar-group labels
|
||||
labels = np.asarray(
|
||||
_rust_continuous_bar_labels(n, int(period_bars)),
|
||||
dtype=np.int64,
|
||||
)
|
||||
ro, rh, rl, rc, rv = _rust_ohlcv_agg(o, h, lo, c, v, labels)
|
||||
return (
|
||||
np.asarray(ro, dtype=np.float64),
|
||||
np.asarray(rh, dtype=np.float64),
|
||||
np.asarray(rl, dtype=np.float64),
|
||||
np.asarray(rc, dtype=np.float64),
|
||||
np.asarray(rv, dtype=np.float64),
|
||||
)
|
||||
Reference in New Issue
Block a user