Unstable Period
TL;DR: Some indicators need a number of bars before their output settles. TA-Lib can discard those bars for you, and one setting covers every function that has them.
Why it exists
Some indicators have "memory". Each output depends on the previous one, seeded from the start of the data. An Exponential Moving Average is the classic example: the seed's effect is large at first, and decays with each bar until the result is stable.
PREC_4.This is inherent to the algorithms, not something specific to TA-Lib: every implementation has to seed the recursion somewhere.
What to do
There are three distinct approaches, from the most common to the most rigorous:
Ignore the problem. What most charting sites do, and usually fine: the latest bar has plenty of history behind it. Nothing warns you when it doesn't; a short series, or a back-test on the earliest bars, quietly uses bad values.
Scrub it yourself. TA-Lib stays at its default (unstable period
0) and returns everything it can compute; your code decides how many leading outputs to drop.Let TA-Lib do it. Set an unstable period and the function stops emitting the leading values the seed still distorts. The simplest way is Auto-Stabilization: a one-time setting that does it all. Advanced Fine-Tuning covers the rest: a stricter level, or your own number of bars, for all functions or for one.
Auto-Stabilization (Recommended)
With Auto-Stabilization, each function works out by itself how many leading bars to discard. The easiest way is to enable it once, for every function, at initialization:
/* Once, after TA_Initialize: */
TA_SetUnstablePeriod( TA_FUNC_UNST_ALL, TA_UNSTABLE_AUTO_PREC_4 );use ta_lib::{Core, FuncUnstId};
let core = Core::builder()
.unstable_period(FuncUnstId::ALL, Core::UNSTABLE_AUTO_PREC_4)
.build()?;import io.github.talib.Core;
import io.github.talib.FuncUnstId;
Core core = Core.builder()
.unstablePeriod(FuncUnstId.ALL, Core.UNSTABLE_AUTO_PREC_4)
.build();using TALib;
Core core = Core.Builder()
.UnstablePeriod(FuncUnstId.ALL, Core.UnstableAutoPrec4)
.Build();import talib
# Once, after import:
talib.set_unstable_period('ALL', talib.UNSTABLE_AUTO_PREC_4)In C and Python the setting is process-wide. In Rust, Java and C# it belongs to core: make your calls on that object.
The number of bars comes from the parameters of each call: a 200-bar EMA drops more than a 30-bar one. Indicators built on another, such as MACD on EMA, follow along.
PREC_4 is a level: it targets 4 significant digits. Once those bars are gone, the first four digits of a value (45.67, 1.234, 0.0001234) no longer depend on where your data starts. For an oscillator that crosses zero, such as MACD, count the digits from the size of its swing.
The lookback is the number of bars before the first output. It grows mostly with the period:
| Call | Lookback at 0 | Lookback at PREC_4 |
|---|---|---|
| EMA(30) | 29 | 179 |
| EMA(200) | 199 | 1199 |
| RSI(14) | 14 | 154 |
| MACD(12, 26, 9) | 33 | 208 |
Use the usual lookback function to find the number for a call. Without more history than that, a batch call succeeds with no output and a stream cannot open.
PREC_4 is a numerical target, and typical market conditions meet it. It is not a guarantee: some functions fall short when prices stay flat, hold a narrow range or climb steadily for many bars, and for some of those no number of bars is enough. The count behind each function, and the known exceptions, are in the specification.
Advanced Fine-Tuning
For finer control, set the unstable period yourself: choose its value, and the functions it applies to.
- The value is an Auto level or a fixed number of bars.
- The functions are all of them (
ALL) or one. Each function with an unstable period has its own id.
TA_RetCode TA_SetUnstablePeriod( TA_FuncUnstId id, unsigned int unstablePeriod );
unsigned int TA_GetUnstablePeriod( TA_FuncUnstId id );
/* Every affected function on Auto, to 4 significant digits: */
TA_SetUnstablePeriod( TA_FUNC_UNST_ALL, TA_UNSTABLE_AUTO_PREC_4 );
/* Then one exception at a stricter level, and one at a fixed 30 bars: */
TA_SetUnstablePeriod( TA_FUNC_UNST_RSI, TA_UNSTABLE_AUTO_PREC_8 );
TA_SetUnstablePeriod( TA_FUNC_UNST_EMA, 30 );
unsigned int v = TA_GetUnstablePeriod( TA_FUNC_UNST_RSI ); /* TA_UNSTABLE_AUTO_PREC_8 */use ta_lib::{Core, FuncUnstId};
// Every affected function on Auto, to 4 significant digits;
// then one exception at a stricter level, and one at a fixed 30 bars:
let core = Core::builder()
.unstable_period(FuncUnstId::ALL, Core::UNSTABLE_AUTO_PREC_4)
.unstable_period(FuncUnstId::RSI, Core::UNSTABLE_AUTO_PREC_8)
.unstable_period(FuncUnstId::EMA, 30)
.build()?;
let v = core.get_unstable_period(FuncUnstId::RSI)?; // Core::UNSTABLE_AUTO_PREC_8import io.github.talib.Core;
import io.github.talib.FuncUnstId;
// Every affected function on Auto, to 4 significant digits;
// then one exception at a stricter level, and one at a fixed 30 bars:
Core core = Core.builder()
.unstablePeriod(FuncUnstId.ALL, Core.UNSTABLE_AUTO_PREC_4)
.unstablePeriod(FuncUnstId.RSI, Core.UNSTABLE_AUTO_PREC_8)
.unstablePeriod(FuncUnstId.EMA, 30)
.build();
int v = core.unstablePeriod(FuncUnstId.RSI); // Core.UNSTABLE_AUTO_PREC_8using TALib;
// Every affected function on Auto, to 4 significant digits;
// then one exception at a stricter level, and one at a fixed 30 bars:
Core core = Core.Builder()
.UnstablePeriod(FuncUnstId.ALL, Core.UnstableAutoPrec4)
.UnstablePeriod(FuncUnstId.RSI, Core.UnstableAutoPrec8)
.UnstablePeriod(FuncUnstId.EMA, 30)
.Build();
int v = core.UnstablePeriod(FuncUnstId.RSI); // Core.UnstableAutoPrec8import talib
# Every affected function on Auto, to 4 significant digits;
# then one exception at a stricter level, and one at a fixed 30 bars:
talib.set_unstable_period('ALL', talib.UNSTABLE_AUTO_PREC_4)
talib.set_unstable_period('RSI', talib.UNSTABLE_AUTO_PREC_8)
talib.set_unstable_period('EMA', 30)
v = talib.get_unstable_period('RSI') # talib.UNSTABLE_AUTO_PREC_8The value is one of:
- An Auto level,
TA_UNSTABLE_AUTO_PREC_4orTA_UNSTABLE_AUTO_PREC_8(a constant onCorein Rust, Java and C#, and ontalibin Python): the function works out the number of bars from the call's parameters.PREC_8targets eight significant digits and needs more bars (EMA(30): 314 instead of 179); some outputs cannot show that many, so start withPREC_4. - A count, from
0toTA_INDEX_MAX: a fixed number of bars that is yours to choose. The function discards that many, whatever its parameters.
The default, 0, discards nothing: you get every value the function can compute.
Each id holds its own value and the last call wins, so ALL followed by one id sets everything and then one exception. Reading a setting returns what was set: a level reads back as its constant, not as a number of bars. To learn how many bars a call discards, ask for its lookback.
The setting follows the function wherever it runs: whether you call it directly, or another indicator uses it internally. The EMA id therefore affects EMA itself and every indicator built on one, such as MACD and DEMA, which can drop a different number of bars than EMA.
In C, the unstable period and the candle settings are process-wide. Change them only while no TA function is running and no stream is open (specification). Python raises RuntimeError on a change while a call runs or a stream object is alive.
Functions with an unstable period
ADX, ATR, CMO, DX, EMA, HT_DCPERIOD, HT_DCPHASE, HT_PHASOR, HT_SINE, HT_TRENDLINE, HT_TRENDMODE, KAMA, MAMA, MINUS_DI, MINUS_DM, NATR, PLUS_DI, PLUS_DM, RSI, T3, RMA, HA, RVI, FRAMA, MCGD, VIDYA, STC, SWAK_GAUSS, SWAK_BUTTER, SWAK_HP, SWAK_2PHP, SWAK_BP, FISHER.
| Language | Id | Enum |
|---|---|---|
| C | TA_FUNC_UNST_<NAME> | ta_defs.h |
| Rust | FuncUnstId::<NAME> | types.rs |
| Java | FuncUnstId.<NAME> | FuncUnstId.java |
| C# | FuncUnstId.<NAME> | FuncUnstId.cs |
| Python | '<NAME>' | _common.pxi |
<NAME> may also be ALL, which selects every function above at once.
See also
- Specification: the count behind each function, and the known exceptions
- Core API: C/C++, Rust, Java, C#
- Candlestick Settings