<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 tulipy
Python bindings for Tulip Indicators
Tulipy requires numpy as all inputs and outputs are numpy arrays (dtype=np.float64).
Installation
You can install via pip install tulipy.
If a wheel is not available for your system, you will need to pip install Cython numpy to build from the source distribution.
Usage
Step1:  Information about indicators are exposed as properties
Step2:  Single outputs are returned directly. Indicators returning multiple outputs use
a tuple in the order indicated by the outputs property.
Step3:  Invalid options will throw an InvalidOptionError
Step4:  If inputs of differing sizes are provided, they are right-aligned and trimmed from the left
<ASSISTANT_TASK:>
Python Code:
import numpy as np
import tulipy as ti
ti.TI_VERSION
DATA = np.array([81.59, 81.06, 82.87, 83,    83.61,
                 83.15, 82.84, 83.99, 84.55, 84.36,
                 85.53, 86.54, 86.89, 87.77, 87.29])
Explanation: tulipy
Python bindings for Tulip Indicators
Tulipy requires numpy as all inputs and outputs are numpy arrays (dtype=np.float64).
Installation
You can install via pip install tulipy.
If a wheel is not available for your system, you will need to pip install Cython numpy to build from the source distribution.
Usage
End of explanation
def print_info(indicator):
    print("Type:", indicator.type)
    print("Full Name:", indicator.full_name)
    print("Inputs:", indicator.inputs)
    print("Options:", indicator.options)
    print("Outputs:", indicator.outputs)
print_info(ti.sqrt)
Explanation: Information about indicators are exposed as properties:
End of explanation
ti.sqrt(DATA)
print_info(ti.sma)
ti.sma(DATA, period=5)
Explanation: Single outputs are returned directly. Indicators returning multiple outputs use
a tuple in the order indicated by the outputs property.
End of explanation
try:
    ti.sma(DATA, period=-5)
except ti.InvalidOptionError:
    print("Invalid Option!")
print_info(ti.bbands)
ti.bbands(DATA, period=5, stddev=2)
Explanation: Invalid options will throw an InvalidOptionError:
End of explanation
DATA2 = np.array([83.15, 82.84, 83.99, 84.55, 84.36])
# 'high' trimmed to DATA[-5:] == array([ 85.53,  86.54,  86.89,  87.77,  87.29])
ti.aroonosc(high=DATA, low=DATA2, period=2)
Explanation: If inputs of differing sizes are provided, they are right-aligned and trimmed from the left:
End of explanation
<END_TASK>