Indicators¶
Question
Learn more in Indicators documentation.
Listing¶
To list the currently supported indicators, use IndicatorFactory.list_indicators. The returned indicator names can be filtered by location, which can be listed with IndicatorFactory.list_locations, or by evaluating a glob or regex pattern.
indicator_names = vbt.IF.list_indicators() # (1)!
indicator_names = vbt.IF.list_indicators("vbt") # (2)!
indicator_names = vbt.IF.list_indicators("talib") # (3)!
indicator_names = vbt.IF.list_indicators("RSI*") # (4)!
indicator_names = vbt.IF.list_indicators("*ma") # (5)!
indicator_names = vbt.IF.list_indicators("[a-z]+ma$", use_regex=True) # (6)!
indicator_names = vbt.IF.list_indicators("*ma", location="pandas_ta") # (7)!
location_names = vbt.IF.list_locations() # (8)!
- List all indicators
- List all custom implemented VBT indicators
- List all TA-Lib indicators
- List any indicators that start with "RSI", such as "RSI" and "RSIIndicator"
- List any indicators that end with "ma", such as "MA" and "SMA"
- Same as above but as a regular expression
- Same as above but only offered by Pandas TA
- List all locations
Note
Without specifying a location, indicators across all the locations will be parsed, which may take some time. Thus, make sure to not repeatedly call this function; instead, save the results to a variable.
+
To get the class of an indicator, use IndicatorFactory.get_indicator.
vbt.BBANDS # (1)!
BBANDS = vbt.IF.get_indicator("pandas_ta:BBANDS") # (2)!
BBANDS = vbt.indicator("pandas_ta:BBANDS") # (3)!
BBANDS = vbt.IF.from_pandas_ta("BBANDS") # (4)!
BBANDS = vbt.pandas_ta("BBANDS") # (5)!
RSI = vbt.indicator("RSI") # (6)!
- Custom implemented VBT indicators can be accessed directly
- Get the indicator class by name as returned by
list_indicators - Same as above
- Same as above by calling the respective function
- Same as above
- Search for an indicator with the name "RSI" and return the first found. Custom, TA-LIB, and Pandas-TA indicators are preferred, in this order.
+
To get familiar with an indicator class, call phelp on the run class method, which is used to run the indicator. Alternatively, the specification such as input names is also available via various properties to be accessed in a programmable fashion.
vbt.phelp(vbt.OLS.run) # (1)!
print(vbt.OLS.input_names) # (2)!
print(vbt.OLS.param_names) # (3)!
print(vbt.OLS.param_defaults) # (4)!
print(vbt.OLS.in_output_names) # (5)!
print(vbt.OLS.output_names) # (6)!
print(vbt.OLS.lazy_output_names) # (7)!
- Get the signature of the
runclass method along with the specification - Get the input names, such as "close" in
talib:SMA. These are the arrays based on which the indicator is computed. - Get the parameter names, such as "timeperiod" in
talib:SMA. These are the parameters that are being tested. - Get the parameter defaults
- Get the in-output names, such as "stop_ts" in
vbt:STX. These are the arrays that are written in-place by the indicator. - Get the output names, such as "entries" and "exits" in
vbt:RANDNX. These are the arrays that are returned by the indicator. - Get the lazy output names, such as "bandwidth" in
vbt:BBANDS. These are the arrays that can be optionally calculated after the indicator has finished computing.
Running¶
To run an indicator, call the IndicatorBase.run class method of its class by manually passing the input arrays (which can be any array-like objects such as Pandas DataFrames and NumPy arrays), parameters (which can be single values and lists for testing multiple parameter combinations), and other arguments expected by the indicator. The result of running the indicator is an indicator instance (not the actual arrays!).
bbands = vbt.BBANDS.run(close) # (1)!
bbands = vbt.BBANDS.run(open) # (2)!
bbands = vbt.BBANDS.run(close, window=20) # (3)!
bbands = vbt.BBANDS.run(close, window=vbt.Default(20)) # (4)!
bbands = vbt.BBANDS.run(close, window=20, hide_params=["window"]) # (5)!
bbands = vbt.BBANDS.run(close, window=20, hide_params=True) # (6)!
bbands = vbt.BBANDS.run(close, window=[10, 20, 30]) # (7)!
bbands = vbt.BBANDS.run(close, window=[10, 20, 30], alpha=[2, 3, 4]) # (8)!
bbands = vbt.BBANDS.run(close, window=[10, 20, 30], alpha=[2, 3, 4], param_product=True) # (9)!
- Run the indicator on close price and with default parameters
- Run the indicator on open price. You can pass any time series if the indicator expects only "close". But as soon as it expects other time series such as "open", "high", or "low", use only "close".
- Test a window of 20. If the close price is a DataFrame, this will append a new column level called "window". If the close price is a Series, it's name will become a tuple.
- To avoid adding a new column level, wrap with
Default - Alternatively, hide the parameter
- Hide all parameters. Makes only sense when only one parameter combination is being tested, and you want the output arrays to have the same columns/name as the input arrays.
- Test three windows. This will produce three Series/DataFrames stacked into one DataFrame along columns.
- Test three parameter combinations: (10, 2), (20, 3) and (30, 4)
- Test all combinations between both parameters, leading to overall 9 tests
Warning
Testing a wide grid of parameter combinations will produce wide arrays. For example, testing 10000 parameter combinations on one year of daily data would produce an array that takes 30MB of RAM. If the indicator returns three arrays, the RAM consumption would be at least 120MB. One year of minute data would result in staggering 40GB. Thus, for testing wide parameter grids it's recommended to test only a subset of combinations at a time, such as with the use of parameterization or chunking.
+
Often, there's a need to make an indicator skip missing values. For this, use skipna=True. This argument not only works for TA-Lib indicators but for any indicators, the only requirement: the jitted loop must be disabled. Also, when a two-dimensional input array is passed, you need to additionally pass split_columns=True to split its columns and process one column at once.
bbands = vbt.BBANDS.run(close_1d, skipna=True)
bbands = vbt.BBANDS.run(close_2d, split_columns=True, skipna=True)
+
Another way is to remove missing values altogether.
bbands = bbands.dropna() # (1)!
bbands = bbands.dropna(how="all") # (2)!
- Remove any row that has at least one missing value across all outputs
- Remove any row that has all values missing across all outputs
+
To retrieve the output arrays from an indicator instance, either access each as an attribute, or use various unpacking options such as IndicatorBase.unpack.
bbands = vbt.talib("BBANDS").run(close)
upperband_df = bbands.upperband # (1)!
middleband_df = bbands.middleband
lowerband_df = bbands.lowerband
upperband_df, middleband_df, lowerband_df = bbands.unpack() # (2)!
output_dict = bbands.to_dict() # (3)!
output_df = bbands.to_frame() # (4)!
sma = vbt.talib("SMA").run(close)
sma_df = sma.real # (5)!
sma_df = sma.sma # (6)!
sma_df = sma.output # (7)!
sma_df = sma.unpack()
- Retrieve the upper band array from the indicator instance
- Retrieve the output arrays in the same order as they are in
bbands.output_names - Retrieve the dictionary with the output arrays
- Retrieve a DataFrame with the output arrays stacked along columns
- If a TA-Lib indicator has only one output array, it's usually named "real"
- The only output can also be accessed by the short name of the indicator
- Also, the only output has an alias "output"
+
To keep outputs in the NumPy format and/or omit any shape checks, use return_raw="outputs".
upperband, middleband, lowerband = vbt.talib("BBANDS").run(close, return_raw="outputs")
+
An even simpler way to run indicators is by using Data.run, which takes an indicator name or class, identifies what input names the indicator expects, and then runs the indicator while passing all the inputs found in the data instance automatically. This method also allows unpacking and running multiple indicators, which is very useful for feature engineering.
bbands = data.run("vbt:BBANDS") # (1)!
bbands = data.run("vbt:BBANDS", window=20) # (2)!
upper, middle, lower = data.run("vbt:BBANDS", unpack=True) # (3)!
features_df = data.run(["talib:BBANDS", "talib:RSI"]) # (4)!
bbands, rsi = data.run(["talib:BBANDS", "talib:RSI"], concat=False) # (5)!
features_df = data.run( # (6)!
["talib:BBANDS", "talib:RSI"],
timeperiod=vbt.run_func_dict(talib_bbands=20, talib_rsi=30),
hide_params=True
)
features_df = data.run( # (7)!
["talib:BBANDS", "vbt:RSI"],
talib_bbands=vbt.run_arg_dict(timeperiod=20),
vbt_rsi=vbt.run_arg_dict(window=30),
hide_params=True
)
features_df = data.run("talib_all") # (8)!
- Run
vbt.BBANDSwith the "close" input name automatically substituted bydata.close - Any arguments will be passed down to the indicator's
runclass method - The output arrays can be directly unpacked. The argument
unpackalso accepts the options "dict" and "frame". - Run multiple indicators. By default, they will be concatenated into a DataFrame along columns. The function and output names will be visible in the column levels "run_func" and "output" respectively.
- Do not concatenate indicators but return them as a list
- Pass different values under the same parameter name using
run_func_dict - Pass different keyword arguments to each indicator using
run_arg_dict - Run all TA-Lib indicators
+
To quickly run and plot a TA-Lib indicator on a single parameter combination without using the indicator factory, use talib_func and talib_plot_func respectively. In contrast to the official TA-Lib implementation, it can properly handle DataFrames, NaNs, broadcasting, and timeframes. The indicator factory's TA-Lib version is based on these two functions.
run_bbands = vbt.talib_func("BBANDS")
upperband, middleband, lowerband = run_bbands(close, timeperiod=2)
upperband, middleband, lowerband = data.run("talib_func:BBANDS", timeperiod=2) # (1)!
plot_bbands = vbt.talib_plot_func("BBANDS")
fig = plot_bbands(upperband, middleband, lowerband)
- Same as above
Parallelization¶
Parameter combinations are processed using execute such that it's fairly easy to parallelize their execution.
any_indicator.run(...) # (1)!
# ______________________________________________________________
numba_indicator.run( # (2)!
...,
jitted_loop=True, # (3)!
jitted_warmup=True, # (4)!
execute_kwargs=dict(n_chunks="auto", engine="threadpool")
)
# ______________________________________________________________
python_indicator.run( # (5)!
...,
execute_kwargs=dict(n_chunks="auto", distribute="chunks", engine="pathos")
)
- By default, the method processes parameter combinations serially (i.e., not in parallel)
- If the indicator is compiled with Numba and
nogilis enabled, divide parameter combinations into an optimal number of chunks and execute all chunks in parallel with multithreading (i.e., one chunk per thread) - Wrap the loop with Numba that goes over parameter combinations (required)
- Dry-run one parameter combination to compile the indicator ahead of the distribution (optional)
- If the indicator is not compiled with Numba, divide parameter combinations into an optimal number of chunks, and execute all chunks in parallel with multiprocessing (i.e., one chunk per process) while executing all parameter combinations within each chunk serially
Registration¶
Custom indicators can be registered by the indicator factory to appear in the list of all indicators. This is convenient to be able to refer to the indicator by its name when running a data instance. Upon registration, you can assign the indicator to a custom location (the default location is "custom"), which acts as a tag or group; this can be used to build arbitrary indicator groups. One indicator can be assigned to multiple locations. Custom indicators have priority over built-in indicators.
vbt.IF.register_custom_indicator(sma_indicator) # (1)!
vbt.IF.register_custom_indicator(sma_indicator, "SMA") # (2)!
vbt.IF.register_custom_indicator(sma_indicator, "SMA", location="rolling") # (3)!
vbt.IF.register_custom_indicator(sma_indicator, "rolling:SMA")
vbt.IF.register_custom_indicator("talib:sma", location="rolling") # (4)!
vbt.IF.deregister_custom_indicator("SMA", location="rolling") # (5)!
vbt.IF.deregister_custom_indicator("rolling:SMA")
vbt.IF.deregister_custom_indicator("SMA") # (6)!
vbt.IF.deregister_custom_indicator(location="rolling") # (7)!
vbt.IF.deregister_custom_indicator() # (8)!
- Register an indicator class. The class name will become the indicator name.
- Register an indicator class with a specific name
- Register an indicator class with a specific name and under a specific location
- Assign a built-in indicator class to a custom location (i.e., tagging)
- Deregister all indicators with a specific name under a specific location
- Deregister all indicators with a specific name under all locations
- Deregister all indicators under a specific location
- Deregister all indicators under all locations