talib/_common.pxi builds _ta_func_unst_ids from a hardcoded list:
for i, name in enumerate(['ADX', 'ADXR', 'ATR', ..., 'T3', 'ALL']):
_ta_func_unst_ids[name] = i
That list is the TA-Lib 0.4.0 numbering. The C enum was renumbered in 0.6.0 (TA_FUNC_UNST_IMI inserted at slot 12), so against any TA-Lib >= 0.6.0 everything from slot 12 up targets the wrong function. setup.py already requires >= 0.6.1, so this is the normal install.
Measured against libta-lib.so 0.8.1 (ctypes, using the exact ids the table produces):
set_unstable_period(name, n) |
id sent |
actually sets |
| ADX … HT_TRENDMODE |
0–11 |
correct |
| KAMA |
12 |
(retired slot — no-op) |
| MAMA |
13 |
KAMA |
| MFI |
14 |
MAMA |
| MINUS_DI |
15 |
(retired slot — no-op) |
| MINUS_DM |
16 |
MINUS_DI |
| NATR |
17 |
MINUS_DM |
| PLUS_DI |
18 |
NATR |
| PLUS_DM |
19 |
PLUS_DI |
| RSI |
20 |
PLUS_DM |
| STOCHRSI |
21 |
RSI |
| T3 |
22 |
(retired slot — no-op) |
| ALL |
23 |
only T3 |
So set_unstable_period('RSI', n) has no effect on RSI, and set_unstable_period('ALL', n) sets one function.
Why it went unnoticed for ~2 years: set and get use the same wrong id, so every round-trip test passes. Only a behavioural check catches it — e.g. set_unstable_period(name, k) must add exactly k leading NaNs to that function's output.
Fix — stop hardcoding. _ta_lib.pxd already externs the enum, and Cython takes extern const values from the C header at build time, ignoring the literal written in the .pxd (verified: an enumerator declared = 999 in a pxd compiled to the header's 21). Building the dict from lib.TA_FUNC_UNST_* is therefore correct against every TA-Lib version, with no version detection.
Two things fall out of it:
-
_ta_lib.pxd:50 declares TA_FUNC_UNST_HD_PHASOR (typo for HT_PHASOR). Harmless while unreferenced; referencing it fails the C compile. Its STOCHRSI = 21 / T3 = 21 duplicate and ALL = 22 are also stale, though ignored.
-
ADXR, MFI and STOCHRSI are no longer C enumerators (TA_FUNC_UNST_UNUSED_1/15/22 since 0.6.0), so their ids cannot come from the header. Note their slots were never read even before that — which is why retiring them was not a functional change. ADXR and STOCHRSI do follow their inner indicator, but through the implementation rather than a knob: TA_ADXR_Lookback calls TA_ADX_Lookback and TA_STOCHRSI_Lookback calls TA_RSI_Lookback, so setting 'ADX' / 'RSI' already moves them. Suggestion: keep all three keys as deprecated no-ops that emit a DeprecationWarning naming the real knob. Aliasing them to ADX/RSI instead would turn a long-standing no-op into a call with side effects on other functions.
Happy to open the PR (dict from the externed constants, the pxd typo, and the NaN-count behavioural test).
talib/_common.pxibuilds_ta_func_unst_idsfrom a hardcoded list:That list is the TA-Lib 0.4.0 numbering. The C enum was renumbered in 0.6.0 (
TA_FUNC_UNST_IMIinserted at slot 12), so against any TA-Lib >= 0.6.0 everything from slot 12 up targets the wrong function.setup.pyalready requires >= 0.6.1, so this is the normal install.Measured against
libta-lib.so0.8.1 (ctypes, using the exact ids the table produces):set_unstable_period(name, n)So
set_unstable_period('RSI', n)has no effect on RSI, andset_unstable_period('ALL', n)sets one function.Why it went unnoticed for ~2 years: set and get use the same wrong id, so every round-trip test passes. Only a behavioural check catches it — e.g.
set_unstable_period(name, k)must add exactlykleading NaNs to that function's output.Fix — stop hardcoding.
_ta_lib.pxdalready externs the enum, and Cython takes externconstvalues from the C header at build time, ignoring the literal written in the.pxd(verified: an enumerator declared= 999in a pxd compiled to the header's21). Building the dict fromlib.TA_FUNC_UNST_*is therefore correct against every TA-Lib version, with no version detection.Two things fall out of it:
_ta_lib.pxd:50declaresTA_FUNC_UNST_HD_PHASOR(typo forHT_PHASOR). Harmless while unreferenced; referencing it fails the C compile. ItsSTOCHRSI = 21/T3 = 21duplicate andALL = 22are also stale, though ignored.ADXR,MFIandSTOCHRSIare no longer C enumerators (TA_FUNC_UNST_UNUSED_1/15/22since 0.6.0), so their ids cannot come from the header. Note their slots were never read even before that — which is why retiring them was not a functional change. ADXR and STOCHRSI do follow their inner indicator, but through the implementation rather than a knob:TA_ADXR_LookbackcallsTA_ADX_LookbackandTA_STOCHRSI_LookbackcallsTA_RSI_Lookback, so setting'ADX'/'RSI'already moves them. Suggestion: keep all three keys as deprecated no-ops that emit aDeprecationWarningnaming the real knob. Aliasing them to ADX/RSI instead would turn a long-standing no-op into a call with side effects on other functions.Happy to open the PR (dict from the externed constants, the pxd typo, and the NaN-count behavioural test).