146 lines
5.1 KiB
Markdown
146 lines
5.1 KiB
Markdown
# ferro-ta ↔ finta Compatibility
|
||
|
||
[finta](https://github.com/peerchemist/finta) implements over 80 financial
|
||
technical indicators as class methods on a single `TA` class, operating
|
||
entirely on Pandas DataFrames.
|
||
|
||
---
|
||
|
||
## Key architectural differences
|
||
|
||
| Aspect | ferro-ta | finta |
|
||
|--------|---------|-------|
|
||
| **Backend** | Rust/C + SIMD | Pure Pandas |
|
||
| **Input type** | NumPy array or list | OHLCV Pandas DataFrame (required) |
|
||
| **DatetimeIndex** | Not required | **Required** |
|
||
| **Column names** | Separate arrays | `open/high/low/close/volume` |
|
||
| **Output type** | NumPy array | Pandas Series or DataFrame |
|
||
| **NaN handling** | Pads warmup with NaN | Pads warmup with NaN |
|
||
| **Streaming** | Yes (StreamingXxx classes) | No |
|
||
| **Speed** | ~700× faster on ATR | Baseline (pure Pandas) |
|
||
|
||
---
|
||
|
||
## Required DataFrame format
|
||
|
||
finta requires a **Pandas DataFrame with a DatetimeIndex** and lowercase
|
||
column names:
|
||
|
||
```python
|
||
import pandas as pd
|
||
import numpy as np
|
||
|
||
df = pd.DataFrame({
|
||
"open": open_prices,
|
||
"high": high_prices,
|
||
"low": low_prices,
|
||
"close": close_prices,
|
||
"volume": volume_data, # required for volume indicators
|
||
}, index=pd.date_range("2020-01-01", periods=len(close_prices), freq="D"))
|
||
```
|
||
|
||
ferro-ta accepts raw NumPy arrays or Python lists — no DataFrame needed.
|
||
|
||
---
|
||
|
||
## Function signature mapping
|
||
|
||
finta uses a class-method API: `TA.INDICATOR(ohlcv_df, period, ...)`.
|
||
|
||
| Indicator | ferro-ta | finta |
|
||
|-----------|---------|-------|
|
||
| SMA | `SMA(close, timeperiod=20)` | `TA.SMA(df, 20)` |
|
||
| EMA | `EMA(close, timeperiod=20)` | `TA.EMA(df, 20)` |
|
||
| WMA | `WMA(close, timeperiod=14)` | `TA.WMA(df, 14)` |
|
||
| DEMA | `DEMA(close, timeperiod=30)` | `TA.DEMA(df, 30)` |
|
||
| TEMA | `TEMA(close, timeperiod=30)` | `TA.TEMA(df, 30)` |
|
||
| HMA | Not supported | `TA.HMA(df, 16)` |
|
||
| RSI | `RSI(close, timeperiod=14)` | `TA.RSI(df, 14)` |
|
||
| MACD | `MACD(close, 12, 26, 9)` → (macd, signal, hist) | `TA.MACD(df, 12, 26, 9)` → DataFrame with `MACD`/`SIGNAL` columns |
|
||
| BBANDS | `BBANDS(close, 20, 2.0, 2.0)` → (upper, mid, lower) | `TA.BBANDS(df, 20)` → DataFrame with `BB_UPPER`/`BB_MIDDLE`/`BB_LOWER` |
|
||
| ATR | `ATR(high, low, close, timeperiod=14)` | `TA.ATR(df, 14)` |
|
||
| TRUE RANGE | `TRANGE(high, low, close)` | `TA.TR(df)` |
|
||
| OBV | `OBV(close, volume)` | `TA.OBV(df)` |
|
||
| MFI | `MFI(high, low, close, volume, timeperiod=14)` | `TA.MFI(df, 14)` |
|
||
| CCI | `CCI(high, low, close, timeperiod=14)` | `TA.CCI(df, 14)` |
|
||
| STOCH | `STOCH(high, low, close, 5, 3, 3)` | `TA.STOCH(df, 14)` |
|
||
| WILLR | `WILLR(high, low, close, timeperiod=14)` | `TA.WILLIAMS(df, 14)` |
|
||
| ADX | `ADX(high, low, close, timeperiod=14)` | `TA.ADX(df, 14)` |
|
||
| AROON | `AROON(high, low, timeperiod=14)` → (up, down) | `TA.AROON(df, 14)` → DataFrame |
|
||
|
||
---
|
||
|
||
## Numerical accuracy
|
||
|
||
finta uses sample standard deviation (ddof=1) for Bollinger Bands while
|
||
ferro-ta follows the TA-Lib convention (population std, ddof=0). For a
|
||
window of 20 bars this creates a ~0.5% difference in band width.
|
||
|
||
For EMA-based indicators, finta seeds with the first data point while ferro-ta
|
||
follows TA-Lib (SMA of first `timeperiod` bars). Values converge after
|
||
~3× the period.
|
||
|
||
Cross-library correlation between ferro-ta and finta is ≥ 0.95 for all
|
||
indicators after discarding the warm-up period.
|
||
|
||
---
|
||
|
||
## Speed comparison
|
||
|
||
On 10,000 bars (median µs, Apple M-series):
|
||
|
||
| Indicator | ferro-ta | finta | ferro-ta speedup |
|
||
|-----------|--------:|-------:|----------------:|
|
||
| SMA | 16.7 | 178.1 | **10.7×** |
|
||
| MACD | 70.4 | 383.9 | **5.5×** |
|
||
| ATR | 51.4 | 1,247 | **24×** |
|
||
|
||
On 100,000 bars:
|
||
|
||
| Indicator | ferro-ta | finta | ferro-ta speedup |
|
||
|-----------|--------:|--------:|----------------:|
|
||
| SMA | 126.2 | 699.7 | **5.6×** |
|
||
| MACD | 465.9 | 1,470.8 | **3.2×** |
|
||
| ATR | 478.5 | 6,782 | **14×** |
|
||
|
||
finta's ATR scales especially poorly because it relies on Pandas `.apply()`
|
||
with a lambda, which cannot be vectorised.
|
||
|
||
---
|
||
|
||
## Migration guide
|
||
|
||
```python
|
||
# FROM finta
|
||
import pandas as pd
|
||
from finta import TA
|
||
|
||
ohlcv = pd.DataFrame(...) # must have DatetimeIndex + open/high/low/close/volume
|
||
sma = TA.SMA(ohlcv, 20) # returns Pandas Series
|
||
macd_df = TA.MACD(ohlcv, 12, 26, 9) # returns DataFrame with MACD/SIGNAL cols
|
||
bb_df = TA.BBANDS(ohlcv, 20) # returns DataFrame with BB_UPPER/MIDDLE/LOWER
|
||
|
||
# TO ferro-ta (NumPy arrays — no DataFrame required)
|
||
import ferro_ta
|
||
import numpy as np
|
||
|
||
close = ohlcv["close"].values
|
||
sma = ferro_ta.SMA(close, timeperiod=20)
|
||
|
||
macd, signal, hist = ferro_ta.MACD(close, fastperiod=12, slowperiod=26, signalperiod=9)
|
||
|
||
upper, middle, lower = ferro_ta.BBANDS(close, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
|
||
```
|
||
|
||
---
|
||
|
||
## Known limitations
|
||
|
||
- finta cannot process raw NumPy arrays — a properly formatted DataFrame with
|
||
DatetimeIndex is always required.
|
||
- `TA.MACD` only returns `MACD` and `SIGNAL` columns; the histogram must be
|
||
computed manually as `MACD - SIGNAL`.
|
||
- Several finta indicators use non-standard formulas that may not match TA-Lib
|
||
conventions (e.g. STOCH uses a fixed 14-period window regardless of the
|
||
`fastk_period` argument).
|