Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Feature

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.

The following code snippet shows the essence of the Feature abstract base class:

class Feature(Generic[T]):
   
    @abstractmethod
    def calc(self, value: T) -> NDArray[np.float32]:
        ...

    @abstractmethod
    def size(self) -> int:
        ...

As can seen in the above snippet, a feature calculation always returns an Numpy array of the type float32. In fact it is aways 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.

In roboquant there are three types of feature implementations included:

  1. Features that calculate based on an event

  2. Features that calculate based on an account

  3. Generic Features that wrap other features, for example fill missing values or don’t rely rely on input data at all.

Event-Based Features

FeatureDescription
DayOfWeekFeatureDay of week when the event took place
DayOfMonthFeatureDay of month when the event took place
MonthOfYearFeatureMonth of year when the event took place
TimeDifferenceTime difference between two events
IndicatorFeatureBase class for own indicators
TrueRangeFeatureCalculate True Range
PriceFeatureExtract the prices for one or more assets
BarFeatureExtract the bar prices for one or more assets
QuoteFeatureExtract the quotes for one or more assets
CacheFeatureCache other event feature
VolumeFeatureExtract 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

FeatureDescription
EquityFeatureTotal Equity
UnrealizedPNLFeatureUnrelaized Profit & Loss

Generic Features

FeatureDescription
SlicedFeatureSlice another feature
FixedValueFeatureFeature of fixed values
RandomFeatureFeature that generates random values
FeatureSetCombine other features into a new feature
NormalizeFeatureNormalize data over a certain period
FillFeatureFill in missing (“nan”) values iwht last known value
FillWithConstantFeatureFill missing values with a constant float value
ReturnFeatureCalculate the next step return
LongReturnsFeatureCalculate the return over a longer period
MaxReturnFeatureCalculate the maximum return over certain period
MinReturnFeatureCalculate the minimal return over certain period
SMAFeatureCalculate 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])