Feature
Overview¶
One of the compelling arguments to use Python for algo-trading, is the relative ease of using AI and machine learning as part of your solution. That being said, if you are new to Python development, it might still seem a bit daunting at first.
Feature classes in roboquant transform data into structured input features and target labels for AI/ML models. They serve as the bridge between the time-series domain of financial data and the tabular world of machine learning algorithms and help to lower the learning curve.
API¶
The following code snippet shows the essence of the Feature abstract base class:
class Feature(ABC, Generic[T]):
@abstractmethod
def calc(self, value: T) -> NDArray[np.float32]:
...
@abstractmethod
def size(self) -> int:
...
def reset(self):
pass
As can be seen in the above snippet, a feature calculation always returns a Numpy array of the type float32. In fact, it is always a 1-dimensional float32 array and missing values are represented as float “NaN” values in that array. Every invocation should always return the same length array.
If a feature has state, it should override the reset method and clear that state. So that during the training of the next epoch it starts fresh and doesn’t carry over state from the previous epoch.
Feature Types¶
In roboquant there are three types of feature implementations included:
Features that calculate based on an event
Features that calculate based on an account
Generic Features that wrap other features, for example fill missing values or don’t rely on input data at all.
Event-Based Features¶
| Feature | Description |
|---|---|
DayOfWeekFeature | Day of week when the event took place |
DayOfMonthFeature | Day of month when the event took place |
MonthOfYearFeature | Month of year when the event took place |
TimeDifference | Time difference between two events |
IndicatorFeature | Base class for custom indicator features |
TrueRangeFeature | Calculate the True Range |
PriceFeature | Extract the prices for one or more assets |
BarFeature | Extract the bar prices for one or more assets |
QuoteFeature | Extract the quotes for one or more assets |
CacheFeature | Cache other event feature |
VolumeFeature | Extract the trading volume for one or more assets |
Besides writing a full custom feature, you can also implement the Indicator feature.
from roboquant.util.indicators import RSI
import roboquant as rq
from roboquant.common.asset import Asset
from roboquant.ai.features import IndicatorFeature
from roboquant.util.buffer import OHLCVBuffer
class RSIFeature(IndicatorFeature):
"""Example using TaLib to create a RSI feature"""
def _calc(self, asset: Asset, ohlcv: OHLCVBuffer) -> float:
close = ohlcv.close()
return RSI(close, timeperiod=self.timeperiod - 1)Account-Based Features¶
| Feature | Description |
|---|---|
EquityFeature | Total Equity |
UnrealizedPNLFeature | Unrealized Profit & Loss |
Generic Features¶
| Feature | Description |
|---|---|
SlicedFeature | Slice another feature |
FixedValueFeature | Feature of fixed values |
RandomFeature | Feature that generates random values |
FeatureSet | Combine other features into a new feature |
NormalizeFeature | Normalize data over a certain period |
FillFeature | Fill in missing (“nan”) values with the last known value |
FillWithConstantFeature | Fill missing values with a constant float value |
ReturnFeature | Calculate the next step return |
LongReturnsFeature | Calculate the return over a longer period |
MaxReturnFeature | Calculate the maximum return over certain period |
MinReturnFeature | Calculate the minimal return over certain period |
SMAFeature | Calculate the Simple Moving Average over the result of another Feature |
Custom Features¶
For bespoke logic, subclass Feature and implement the calc() and size() method.
The following example shows how to develop a feature that calculates the spreads between
two different assets.
from roboquant import Event, Asset
from roboquant.ai.features import Feature
import numpy as np
class SpreadFeature(Feature[Event]):
"""This feature calculates the spread between two different assets"""
def __init__(self, asset_a: Asset, asset_b: Asset):
self.asset_a = asset_a
self.asset_b = asset_b
def size(self):
return 1
def calc(self, event):
px_a = event.get_price(self.asset_a)
px_b = event.get_price(self.asset_b)
if px_a is None or px_b is None:
result = float("nan")
else:
result = px_a - px_b
return np.array([result])