Core Module¶
signalflow.core ¶
SignalFlow Core Module.
Provides fundamental building blocks for SignalFlow trading framework: - Containers: RawData, Signals, Position, Trade, Portfolio, Order, OrderFill - Enums: SignalType, SfComponentType, PositionType, etc. - Registry: Component registration and discovery - Decorators: Semantic decorators for component registration - @sf.detector, @sf.feature, @sf.validator, @sf.labeler - @sf.entry, @sf.exit - @sf.signal_metric, @sf.strategy_metric - @sf.alert, @sf.data_source, @sf.data_store, @sf.executor, @sf.risk - Transforms: SignalsTransform protocol
RawData
dataclass
¶
RawData(datetime_start: datetime, datetime_end: datetime, pairs: list[str] = list(), data: dict[str, DataFrame | dict[str, DataFrame]] = dict(), default_source: str | None = None)
Immutable container for raw market data.
Acts as a unified in-memory bundle for multiple raw datasets (e.g. spot prices, funding, trades, orderbook, signals).
Design principles
- Canonical storage is dataset-based (dictionary by name)
- Datasets accessed via string keys (e.g. raw_data["spot"])
- No business logic or transformations
- Immutability ensures reproducibility in pipelines
Supports two data structures
- Flat: dict[str, pl.DataFrame] - single source per data type
- Nested: dict[str, dict[str, pl.DataFrame]] - multi-source per data type
Attributes:
| Name | Type | Description |
|---|---|---|
datetime_start |
datetime
|
Start datetime of the data snapshot. |
datetime_end |
datetime
|
End datetime of the data snapshot. |
pairs |
list[str]
|
List of trading pairs in the snapshot. |
data |
dict
|
Dictionary of datasets. Can be flat or nested. |
default_source |
str | None
|
Default source for nested data. |
Example
from signalflow.core import RawData
import polars as pl
from datetime import datetime
# Flat structure (single source)
raw_data = RawData(
datetime_start=datetime(2024, 1, 1),
datetime_end=datetime(2024, 12, 31),
pairs=["BTCUSDT", "ETHUSDT"],
data={
"spot": spot_dataframe,
"signals": signals_dataframe,
}
)
# Access datasets
spot_df = raw_data["spot"]
signals_df = raw_data.get("signals")
# Nested structure (multi-source)
raw_data = RawData(
datetime_start=datetime(2024, 1, 1),
datetime_end=datetime(2024, 12, 31),
pairs=["BTCUSDT", "ETHUSDT"],
data={
"perpetual": {
"binance": binance_df,
"okx": okx_df,
"bybit": bybit_df,
}
},
default_source="binance",
)
# Hierarchical access
df = raw_data.perpetual.binance # specific source
df = raw_data.perpetual.to_polars() # default with warning
print(raw_data.perpetual.sources) # ["binance", "okx", "bybit"]
# Check if dataset exists
if "spot" in raw_data:
print("Spot data available")
Note
Dataset schemas are defined by convention, not enforced. Views (pandas/polars) should be handled by RawDataView wrapper.
__contains__ ¶
Check if dataset exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Dataset name to check. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if dataset exists, False otherwise. |
Source code in src/signalflow/core/containers/raw_data.py
__getattr__ ¶
Attribute access for hierarchical pattern: raw.perpetual.binance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Data type name. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
DataTypeAccessor |
DataTypeAccessor
|
Accessor for the data type. |
Raises:
| Type | Description |
|---|---|
AttributeError
|
If data type doesn't exist. |
Source code in src/signalflow/core/containers/raw_data.py
__getitem__ ¶
Dictionary-style access to datasets.
Supports both simple key and tuple (data_type, source) indexing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str | tuple[str, str]
|
Dataset name or (data_type, source) tuple. |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
pl.DataFrame: Dataset as Polars DataFrame. |
Example
Source code in src/signalflow/core/containers/raw_data.py
__iter__ ¶
Iterate over data type keys.
Returns:
| Type | Description |
|---|---|
Iterator[str]
|
Iterator[str]: Iterator over dataset names. |
Source code in src/signalflow/core/containers/raw_data.py
get ¶
Get dataset by key.
For nested (multi-source) data, returns default source with warning unless source is explicitly specified.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Dataset name (e.g. "spot", "perpetual"). |
required |
source
|
str | None
|
Source name for nested data. If None, uses default_source with warning. |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
pl.DataFrame: Polars DataFrame if exists, empty DataFrame otherwise. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If dataset exists but is not a valid structure. |
KeyError
|
If source specified but not found. |
Example
# Flat structure
spot_df = raw_data.get("spot")
# Nested structure - explicit source
df = raw_data.get("perpetual", source="binance")
# Nested structure - default source (warns)
df = raw_data.get("perpetual")
# Returns empty DataFrame if key doesn't exist
missing_df = raw_data.get("nonexistent")
assert missing_df.is_empty()
Source code in src/signalflow/core/containers/raw_data.py
items ¶
Return (key, dataset) pairs.
Returns:
| Name | Type | Description |
|---|---|---|
Iterator |
Iterator[tuple[str, DataFrame | dict[str, DataFrame]]]
|
Iterator over (key, DataFrame) tuples. |
Source code in src/signalflow/core/containers/raw_data.py
keys ¶
Return available dataset keys.
Returns:
| Type | Description |
|---|---|
Iterator[str]
|
Iterator[str]: Iterator over dataset names. |
Source code in src/signalflow/core/containers/raw_data.py
sources ¶
Return available sources for a data type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data_type
|
str
|
Data type name (e.g. "perpetual", "spot"). |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
list[str]: List of source names. Returns ["default"] for flat data. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If data type doesn't exist. |
Example
Source code in src/signalflow/core/containers/raw_data.py
values ¶
Return dataset values.
Returns:
| Name | Type | Description |
|---|---|---|
Iterator |
Iterator[DataFrame | dict[str, DataFrame]]
|
Iterator over DataFrames. |
Source code in src/signalflow/core/containers/raw_data.py
Signals
dataclass
¶
Immutable container for trading signals.
Canonical in-memory format is a Polars DataFrame with long schema.
Required columns
- pair (str): Trading pair identifier
- timestamp (datetime): Signal timestamp
- signal_type (SignalType | int): Signal type (RISE, FALL, NONE)
- signal (int | float): Signal value
Optional columns
- probability (float): Signal probability (required for merge logic)
Attributes:
| Name | Type | Description |
|---|---|---|
value |
DataFrame
|
Polars DataFrame containing signal data. |
Example
from signalflow.core import Signals, SignalType
import polars as pl
from datetime import datetime
# Create signals
signals_df = pl.DataFrame({
"pair": ["BTCUSDT", "ETHUSDT"],
"timestamp": [datetime.now(), datetime.now()],
"signal_type": [SignalType.RISE.value, SignalType.FALL.value],
"signal": [1, -1],
"probability": [0.8, 0.7]
})
signals = Signals(signals_df)
# Apply transformation
filtered = signals.apply(filter_transform)
# Chain transformations
processed = signals.pipe(
transform1,
transform2,
transform3
)
# Merge signals
combined = signals1 + signals2
Note
All transformations return new Signals instance. No in-place mutation is allowed.
__add__ ¶
Merge two Signals objects.
Merge rules
- Key: (pair, timestamp) or (pair, timestamp, signal_category) if signal_category column is present.
- Signal type priority:
- null signal_type has lowest priority
- SignalType.NONE ("none") has lowest priority (backward compat)
- Non-null/non-NONE always overrides
- If both non-null,
otherwins - Low-priority signals normalized to probability = 0
- Merge is deterministic
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
Signals
|
Another Signals object to merge. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Signals |
Signals
|
New merged Signals instance. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If other is not a Signals instance. |
Example
Source code in src/signalflow/core/containers/signals.py
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | |
apply ¶
Apply a single transformation to signals.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transform
|
SignalsTransform
|
Callable transformation implementing SignalsTransform protocol. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Signals |
Signals
|
New Signals instance with transformed data. |
Example
Source code in src/signalflow/core/containers/signals.py
pipe ¶
Apply multiple transformations sequentially.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*transforms
|
SignalsTransform
|
Sequence of transformations to apply in order. |
()
|
Returns:
| Name | Type | Description |
|---|---|---|
Signals |
Signals
|
New Signals instance after applying all transformations. |
Source code in src/signalflow/core/containers/signals.py
SignalType ¶
Bases: StrEnum
Enumeration of price direction signal types.
.. deprecated::
SignalType is deprecated and will be removed in a future version.
Use plain string values instead (e.g. "rise", "fall", "flat").
Use null (Polars null) instead of SignalType.NONE for unknown/no signal.
For configuring which signal types to trade, use ``signal_type_map`` on
entry rules or ``DIRECTIONAL_SIGNAL_MAP`` from ``core.signal_registry``.
Represents the direction of a trading signal detected by signal detectors.
This enum covers the PRICE_DIRECTION category. Other categories use
free-form string values for signal_type (see SignalCategory).
Values
NONE: No signal detected. Deprecated -- use null for unknown
or FLAT for sideways market.
RISE: Bullish signal indicating potential price increase.
FALL: Bearish signal indicating potential price decrease.
FLAT: Sideways market / range-bound price action.
Example
Note
Stored as string values in DataFrames for serialization.
For non-PRICE_DIRECTION categories, use string values directly
(e.g. "high_volatility", "local_max") instead of this enum.
SignalFlowRegistry
dataclass
¶
SignalFlowRegistry(_items: dict[SfComponentType, dict[str, ComponentInfo]] = dict(), _raw_data_types: dict[str, set[str]] = (lambda: {k: (v.copy()) for k, v in (_BUILTIN_RAW_DATA_TYPES.items())})(), _discovered: bool = False)
Component registry for dynamic component discovery and instantiation.
Provides centralized registration and lookup for SignalFlow components. Components are organized by type (DETECTOR, EXTRACTOR, etc.) and accessed by case-insensitive names.
Also manages extensible raw data type definitions - each data type maps
to a set of required columns. Built-in types (SPOT, FUTURES, PERPETUAL)
are pre-registered; users can add custom types via register_raw_data_type().
Registry structure
component_type -> name -> ComponentInfo (class + metadata)
Supported component types
- DETECTOR: Signal detection classes
- EXTRACTOR: Feature extraction classes
- LABELER: Signal labeling classes
- ENTRY_RULE: Position entry rules
- EXIT_RULE: Position exit rules
- METRIC: Strategy metrics
- EXECUTOR: Order execution engines
Attributes:
| Name | Type | Description |
|---|---|---|
_items |
dict[SfComponentType, dict[str, ComponentInfo]]
|
Internal storage mapping component types to name-ComponentInfo pairs. |
_raw_data_types |
dict[str, set[str]]
|
Mapping of raw data type names to their required column sets. |
Example
from signalflow.core.registry import SignalFlowRegistry, default_registry
# Register custom raw data type
default_registry.register_raw_data_type(
name="lob",
columns=["pair", "timestamp", "bid", "ask", "bid_size", "ask_size"],
)
# Get component info with docstring for UI
info = default_registry.get_info(SfComponentType.DETECTOR, "sma_cross")
print(info.summary) # "Detects SMA crossover signals."
print(info.docstring) # Full docstring with Args, Example, etc.
# Get columns for any type
cols = default_registry.get_raw_data_columns("spot")
custom_cols = default_registry.get_raw_data_columns("lob")
# List all registered raw data types
print(default_registry.list_raw_data_types())
Note
Component names are stored and looked up in lowercase. Use default_registry singleton for application-wide registration.
See Also
Semantic decorators (@sf.detector, @sf.feature, etc.) for automatic registration.
autodiscover ¶
Scan signalflow.* packages and entry-points for components.
Walks all sub-modules of the signalflow package using
:func:pkgutil.walk_packages and imports them. Because
semantic decorators register classes at import time, importing
a module is sufficient to populate the registry.
External packages can expose components via the
signalflow.components entry-point group. Each entry-point
should reference a module (not a callable); importing it triggers
registration through semantic decorators.
This method is idempotent - subsequent calls are no-ops once
_discovered is True.
Example
Source code in src/signalflow/core/registry.py
create ¶
Instantiate a component by registry key.
Convenient method that combines get() and instantiation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component_type
|
SfComponentType
|
Type of component to create. |
required |
name
|
str
|
Component name (case-insensitive). |
required |
**kwargs
|
Any
|
Arguments to pass to component constructor. |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
Any |
Any
|
Instantiated component. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If component not found. |
TypeError
|
If kwargs don't match component constructor. |
Example
# Create detector with params
detector = registry.create(
SfComponentType.DETECTOR,
"sma_cross",
fast_window=10,
slow_window=20
)
# Create extractor
extractor = registry.create(
SfComponentType.EXTRACTOR,
"rsi",
window=14
)
# Create with config dict
config = {"window": 20, "threshold": 0.7}
labeler = registry.create(
SfComponentType.LABELER,
"fixed",
**config
)
Source code in src/signalflow/core/registry.py
export_schemas ¶
Export schemas for all registered components, grouped by type.
Useful for bulk loading into UI component browsers without N+1 calls.
Returns:
| Type | Description |
|---|---|
dict[str, list[dict[str, Any]]]
|
Dict mapping component type names to lists of component schemas. |
Example
all_schemas = registry.export_schemas() for det in all_schemas.get("DETECTOR", []): ... print(det["name"], len(det["parameters"]), "params")
Source code in src/signalflow/core/registry.py
get ¶
Get a registered class by key.
Lookup is case-insensitive. Raises helpful error with available components if key not found.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component_type
|
SfComponentType
|
Type of component to lookup. |
required |
name
|
str
|
Component name (case-insensitive). |
required |
Returns:
| Type | Description |
|---|---|
type[Any]
|
Type[Any]: Registered class. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If component not found. Error message includes available components. |
Example
# Get component class
detector_cls = registry.get(SfComponentType.DETECTOR, "sma_cross")
# Case-insensitive
detector_cls = registry.get(SfComponentType.DETECTOR, "SMA_Cross")
# Instantiate manually
detector = detector_cls(fast_window=10, slow_window=20)
# Handle missing component
try:
cls = registry.get(SfComponentType.DETECTOR, "unknown")
except KeyError as e:
print(f"Component not found: {e}")
# Shows: "Component not found: DETECTOR:unknown. Available: [sma_cross, ...]"
Source code in src/signalflow/core/registry.py
get_info ¶
Get full component info including docstring.
Use this method when you need metadata for UI display or documentation generation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component_type
|
SfComponentType
|
Type of component to lookup. |
required |
name
|
str
|
Component name (case-insensitive). |
required |
Returns:
| Name | Type | Description |
|---|---|---|
ComponentInfo |
ComponentInfo
|
Full metadata including class, docstring, summary, module. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If component not found. |
Example
# Get info for UI tooltip
info = registry.get_info(SfComponentType.DETECTOR, "sma_cross")
print(info.summary) # "Detects SMA crossover signals."
print(info.docstring) # Full docstring
print(info.module) # "signalflow.detector.sma_cross"
# Use in sf-ui component browser
for name in registry.list(SfComponentType.FEATURE):
info = registry.get_info(SfComponentType.FEATURE, name)
display_component_card(name, info.summary, info.docstring)
Source code in src/signalflow/core/registry.py
get_raw_data_columns ¶
Get required columns for a raw data type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Data type identifier (case-insensitive). Accepts both
|
required |
Returns:
| Type | Description |
|---|---|
set[str]
|
Copy of the column set for the requested type. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If data type is not registered. |
Example
Source code in src/signalflow/core/registry.py
get_schema ¶
Get JSON-serializable parameter schema for a registered component.
Uses dataclasses.fields() to introspect @dataclass-decorated
components and extract field names, types, and defaults.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component_type
|
SfComponentType
|
Type of component. |
required |
name
|
str
|
Component registry name (case-insensitive). |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Schema dict with keys: |
dict[str, Any]
|
|
dict[str, Any]
|
|
Raises:
| Type | Description |
|---|---|
KeyError
|
If component not found. |
Example
schema = registry.get_schema(SfComponentType.DETECTOR, "example/sma_cross") print(schema["description"]) # Short summary print(schema["docstring"]) # Full docstring for UI for p in schema["parameters"]: ... print(f"{p['name']}: {p['type']} = {p['default']}")
Source code in src/signalflow/core/registry.py
list ¶
List registered components for a type.
Returns sorted list of component names for given type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component_type
|
SfComponentType
|
Type of components to list. |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
list[str]: Sorted list of registered component names. |
Example
# List all detectors
detectors = registry.list(SfComponentType.DETECTOR)
print(f"Available detectors: {detectors}")
# Output: ['ema_cross', 'macd', 'rsi_threshold', 'sma_cross']
# Check if component exists
if "sma_cross" in registry.list(SfComponentType.DETECTOR):
detector = registry.create(SfComponentType.DETECTOR, "sma_cross")
# List all component types
from signalflow.core.enums import SfComponentType
for component_type in SfComponentType:
components = registry.list(component_type)
print(f"{component_type.value}: {components}")
Source code in src/signalflow/core/registry.py
list_raw_data_types ¶
List all registered raw data type names.
Returns:
| Type | Description |
|---|---|
list[str]
|
Sorted list of registered data type names. |
register ¶
register(component_type: SfComponentType, name: str, cls: type[Any], *, override: bool = False) -> None
Register a class under (component_type, name).
Stores class with metadata (docstring, module) in registry for later lookup, instantiation, and UI display. Names are normalized to lowercase for case-insensitive lookup.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component_type
|
SfComponentType
|
Type of component (DETECTOR, EXTRACTOR, etc.). |
required |
name
|
str
|
Registry name (case-insensitive, will be lowercased). |
required |
cls
|
Type[Any]
|
Class to register. |
required |
override
|
bool
|
Allow overriding existing registration. Default: False. |
False
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If name is empty or already registered (when override=False). |
Example
# Register new component
registry.register(
SfComponentType.DETECTOR,
name="my_detector",
cls=MyDetector
)
# Override existing component
registry.register(
SfComponentType.DETECTOR,
name="my_detector",
cls=ImprovedDetector,
override=True # Logs warning
)
# Register multiple types
registry.register(SfComponentType.EXTRACTOR, "rsi", RsiExtractor)
registry.register(SfComponentType.LABELER, "fixed", FixedHorizonLabeler)
Source code in src/signalflow/core/registry.py
register_raw_data_type ¶
Register a custom raw data type with its required columns.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Data type identifier (case-insensitive, stored lowercase). |
required |
columns
|
list[str] | set[str]
|
Required column names for this data type. |
required |
override
|
bool
|
Allow overriding an existing registration. |
False
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If name is empty, columns are empty, or name already registered (when override=False). |
Example
Source code in src/signalflow/core/registry.py
snapshot ¶
Snapshot of registry for debugging.
Returns complete registry state organized by component type.
Returns:
| Type | Description |
|---|---|
dict[str, list[str]]
|
dict[str, list[str]]: Dictionary mapping component type names to sorted lists of registered component names. |
Example
# Get full registry snapshot
snapshot = registry.snapshot()
print(snapshot)
# Output:
# {
# 'DETECTOR': ['ema_cross', 'sma_cross'],
# 'EXTRACTOR': ['rsi', 'sma'],
# 'LABELER': ['fixed', 'triple_barrier'],
# 'ENTRY_RULE': ['fixed_size'],
# 'EXIT_RULE': ['take_profit', 'time_based']
# }
# Use for debugging
import json
print(json.dumps(registry.snapshot(), indent=2))
# Check registration status
snapshot = registry.snapshot()
if 'DETECTOR' in snapshot and 'sma_cross' in snapshot['DETECTOR']:
print("SMA detector is registered")
Source code in src/signalflow/core/registry.py
sf_component ¶
Register class as SignalFlow component.
.. deprecated:: 0.6.0 Use semantic decorators instead: - @sf.detector("name") for detectors - @sf.feature("name") for features - @sf.entry("name") for entry rules - @sf.exit("name") for exit rules - etc.
Decorator that registers a class in the global component registry, making it discoverable by name for dynamic instantiation.
The decorated class must have a component_type class attribute
of type SfComponentType to indicate what kind of component it is.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Registry name for the component (case-insensitive). |
required |
override
|
bool
|
Allow overriding existing registration. Default: True. |
True
|
Returns:
| Type | Description |
|---|---|
Callable[[type[Any]], type[Any]]
|
Decorator function that registers and returns the class unchanged. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If class doesn't define component_type attribute. |
Source code in src/signalflow/core/decorators.py
signalflow.core.enums ¶
SfComponentType ¶
Bases: StrEnum
Enumeration of SignalFlow component types.
Defines all component types that can be registered in the component registry. Used by sf_component decorator and SignalFlowRegistry for type-safe registration.
Component categories
- Data: Raw data loading and storage
- Feature: Feature extraction
- Signals: Signal detection, transformation, labeling, validation
- Strategy: Execution, rules, metrics
Values
RAW_DATA_STORE: Raw data storage backends (e.g., DuckDB, Parquet). RAW_DATA_SOURCE: Raw data sources (e.g., Binance API). RAW_DATA_LOADER: Raw data loaders combining source + store. FEATURE: Feature extraction classes (e.g., RSI, SMA). SIGNALS_TRANSFORM: Signal transformation functions. LABELER: Signal labeling strategies (e.g., triple barrier). DETECTOR: Signal detection algorithms (e.g., SMA cross). VALIDATOR: Signal validation models. TORCH_MODULE: PyTorch neural network modules. VALIDATOR_MODEL: Pre-trained validator models. STRATEGY_STORE: Strategy state persistence backends. STRATEGY_RUNNER: Backtest/live runner implementations. STRATEGY_BROKER: Order management and position tracking. STRATEGY_EXECUTOR: Order execution engines (backtest/live). STRATEGY_EXIT_RULE: Position exit rules (e.g., take profit, stop loss). STRATEGY_ENTRY_RULE: Position entry rules (e.g., fixed size). STRATEGY_METRIC: Strategy performance metrics. STRATEGY_ALERT: Strategy monitoring alerts (e.g., max drawdown, stuck positions).
Example
import signalflow as sf
from signalflow.detector import SignalDetector
# Register detector with semantic decorator
@sf.detector("my_detector")
class MyDetector(SignalDetector):
# ... implementation
# Register feature
@sf.feature("my_feature")
class MyFeature(Feature):
# ... implementation
# Register exit rule
@sf.exit("my_exit")
class MyExit(ExitRule):
# ... implementation
# Use in registry
from signalflow.core.registry import default_registry
from signalflow.core.enums import SfComponentType
detector = default_registry.create(
SfComponentType.DETECTOR,
"my_detector"
)
Note
All registered components must have component_type class attribute. Component types are organized hierarchically (category/subcategory).
DataFrameType ¶
Bases: StrEnum
Supported DataFrame backends.
Specifies which DataFrame library to use for data processing. Used by FeatureExtractor and other components to determine input/output format.
Values
POLARS: Polars DataFrame (faster, modern). PANDAS: Pandas DataFrame (legacy compatibility).
Example
from signalflow.core.enums import DataFrameType
from signalflow.feature import FeatureExtractor
# Polars-based extractor
class MyExtractor(FeatureExtractor):
df_type = DataFrameType.POLARS
def extract(self, df: pl.DataFrame) -> pl.DataFrame:
return df.with_columns(
pl.col("close").rolling_mean(20).alias("sma_20")
)
# Pandas-based extractor
class LegacyExtractor(FeatureExtractor):
df_type = DataFrameType.PANDAS
def extract(self, df: pd.DataFrame) -> pd.DataFrame:
df["sma_20"] = df["close"].rolling(20).mean()
return df
# Use in RawDataView
from signalflow.core import RawDataView
view = RawDataView(raw=raw_data)
# Get data in required format
df_polars = view.get_data("spot", DataFrameType.POLARS)
df_pandas = view.get_data("spot", DataFrameType.PANDAS)
Note
New code should prefer POLARS for better performance. PANDAS supported for backward compatibility and legacy libraries.
RawDataType ¶
Bases: StrEnum
Built-in raw data types.
Defines types of market data that can be loaded and processed.
Column definitions are stored in :class:SignalFlowRegistry and can be
extended with custom types via default_registry.register_raw_data_type().
Values
SPOT: Spot trading data (OHLCV). FUTURES: Futures trading data (OHLCV + open_interest). PERPETUAL: Perpetual swaps data (OHLCV + funding_rate + open_interest).
Example
from signalflow.core.enums import RawDataType
# Built-in types
spot_cols = RawDataType.SPOT.columns
# {'pair', 'timestamp', 'open', 'high', 'low', 'close', 'volume'}
# Custom types - register via registry
from signalflow.core.registry import default_registry
default_registry.register_raw_data_type(
name="lob",
columns=["pair", "timestamp", "bid", "ask", "bid_size", "ask_size"],
)
cols = default_registry.get_raw_data_columns("lob")
Note
Use default_registry.register_raw_data_type() to add custom types.
Use default_registry.get_raw_data_columns(name) to look up columns
for any type (built-in or custom).
signalflow.core.registry ¶
default_registry
module-attribute
¶
Global default registry instance.
Use this singleton for application-wide component registration.
Example
from signalflow.core.registry import default_registry
from signalflow.core.enums import SfComponentType
# Register to default registry
default_registry.register(
SfComponentType.DETECTOR,
"my_detector",
MyDetector
)
# Access from anywhere
detector = default_registry.create(
SfComponentType.DETECTOR,
"my_detector"
)
ComponentInfo
dataclass
¶
Metadata about a registered component.
Stores the class reference along with extracted documentation for UI display and introspection.
Attributes:
| Name | Type | Description |
|---|---|---|
cls |
type[Any]
|
The registered component class. |
docstring |
str
|
Full class docstring (or empty string if none). |
summary |
str
|
First line of docstring (short description). |
module |
str
|
Module path where the class is defined. |
from_class
classmethod
¶
Create ComponentInfo by extracting metadata from a class.
Source code in src/signalflow/core/registry.py
SignalFlowRegistry
dataclass
¶
SignalFlowRegistry(_items: dict[SfComponentType, dict[str, ComponentInfo]] = dict(), _raw_data_types: dict[str, set[str]] = (lambda: {k: (v.copy()) for k, v in (_BUILTIN_RAW_DATA_TYPES.items())})(), _discovered: bool = False)
Component registry for dynamic component discovery and instantiation.
Provides centralized registration and lookup for SignalFlow components. Components are organized by type (DETECTOR, EXTRACTOR, etc.) and accessed by case-insensitive names.
Also manages extensible raw data type definitions - each data type maps
to a set of required columns. Built-in types (SPOT, FUTURES, PERPETUAL)
are pre-registered; users can add custom types via register_raw_data_type().
Registry structure
component_type -> name -> ComponentInfo (class + metadata)
Supported component types
- DETECTOR: Signal detection classes
- EXTRACTOR: Feature extraction classes
- LABELER: Signal labeling classes
- ENTRY_RULE: Position entry rules
- EXIT_RULE: Position exit rules
- METRIC: Strategy metrics
- EXECUTOR: Order execution engines
Attributes:
| Name | Type | Description |
|---|---|---|
_items |
dict[SfComponentType, dict[str, ComponentInfo]]
|
Internal storage mapping component types to name-ComponentInfo pairs. |
_raw_data_types |
dict[str, set[str]]
|
Mapping of raw data type names to their required column sets. |
Example
from signalflow.core.registry import SignalFlowRegistry, default_registry
# Register custom raw data type
default_registry.register_raw_data_type(
name="lob",
columns=["pair", "timestamp", "bid", "ask", "bid_size", "ask_size"],
)
# Get component info with docstring for UI
info = default_registry.get_info(SfComponentType.DETECTOR, "sma_cross")
print(info.summary) # "Detects SMA crossover signals."
print(info.docstring) # Full docstring with Args, Example, etc.
# Get columns for any type
cols = default_registry.get_raw_data_columns("spot")
custom_cols = default_registry.get_raw_data_columns("lob")
# List all registered raw data types
print(default_registry.list_raw_data_types())
Note
Component names are stored and looked up in lowercase. Use default_registry singleton for application-wide registration.
See Also
Semantic decorators (@sf.detector, @sf.feature, etc.) for automatic registration.
autodiscover ¶
Scan signalflow.* packages and entry-points for components.
Walks all sub-modules of the signalflow package using
:func:pkgutil.walk_packages and imports them. Because
semantic decorators register classes at import time, importing
a module is sufficient to populate the registry.
External packages can expose components via the
signalflow.components entry-point group. Each entry-point
should reference a module (not a callable); importing it triggers
registration through semantic decorators.
This method is idempotent - subsequent calls are no-ops once
_discovered is True.
Example
Source code in src/signalflow/core/registry.py
create ¶
Instantiate a component by registry key.
Convenient method that combines get() and instantiation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component_type
|
SfComponentType
|
Type of component to create. |
required |
name
|
str
|
Component name (case-insensitive). |
required |
**kwargs
|
Any
|
Arguments to pass to component constructor. |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
Any |
Any
|
Instantiated component. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If component not found. |
TypeError
|
If kwargs don't match component constructor. |
Example
# Create detector with params
detector = registry.create(
SfComponentType.DETECTOR,
"sma_cross",
fast_window=10,
slow_window=20
)
# Create extractor
extractor = registry.create(
SfComponentType.EXTRACTOR,
"rsi",
window=14
)
# Create with config dict
config = {"window": 20, "threshold": 0.7}
labeler = registry.create(
SfComponentType.LABELER,
"fixed",
**config
)
Source code in src/signalflow/core/registry.py
export_schemas ¶
Export schemas for all registered components, grouped by type.
Useful for bulk loading into UI component browsers without N+1 calls.
Returns:
| Type | Description |
|---|---|
dict[str, list[dict[str, Any]]]
|
Dict mapping component type names to lists of component schemas. |
Example
all_schemas = registry.export_schemas() for det in all_schemas.get("DETECTOR", []): ... print(det["name"], len(det["parameters"]), "params")
Source code in src/signalflow/core/registry.py
get ¶
Get a registered class by key.
Lookup is case-insensitive. Raises helpful error with available components if key not found.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component_type
|
SfComponentType
|
Type of component to lookup. |
required |
name
|
str
|
Component name (case-insensitive). |
required |
Returns:
| Type | Description |
|---|---|
type[Any]
|
Type[Any]: Registered class. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If component not found. Error message includes available components. |
Example
# Get component class
detector_cls = registry.get(SfComponentType.DETECTOR, "sma_cross")
# Case-insensitive
detector_cls = registry.get(SfComponentType.DETECTOR, "SMA_Cross")
# Instantiate manually
detector = detector_cls(fast_window=10, slow_window=20)
# Handle missing component
try:
cls = registry.get(SfComponentType.DETECTOR, "unknown")
except KeyError as e:
print(f"Component not found: {e}")
# Shows: "Component not found: DETECTOR:unknown. Available: [sma_cross, ...]"
Source code in src/signalflow/core/registry.py
get_info ¶
Get full component info including docstring.
Use this method when you need metadata for UI display or documentation generation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component_type
|
SfComponentType
|
Type of component to lookup. |
required |
name
|
str
|
Component name (case-insensitive). |
required |
Returns:
| Name | Type | Description |
|---|---|---|
ComponentInfo |
ComponentInfo
|
Full metadata including class, docstring, summary, module. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If component not found. |
Example
# Get info for UI tooltip
info = registry.get_info(SfComponentType.DETECTOR, "sma_cross")
print(info.summary) # "Detects SMA crossover signals."
print(info.docstring) # Full docstring
print(info.module) # "signalflow.detector.sma_cross"
# Use in sf-ui component browser
for name in registry.list(SfComponentType.FEATURE):
info = registry.get_info(SfComponentType.FEATURE, name)
display_component_card(name, info.summary, info.docstring)
Source code in src/signalflow/core/registry.py
get_raw_data_columns ¶
Get required columns for a raw data type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Data type identifier (case-insensitive). Accepts both
|
required |
Returns:
| Type | Description |
|---|---|
set[str]
|
Copy of the column set for the requested type. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If data type is not registered. |
Example
Source code in src/signalflow/core/registry.py
get_schema ¶
Get JSON-serializable parameter schema for a registered component.
Uses dataclasses.fields() to introspect @dataclass-decorated
components and extract field names, types, and defaults.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component_type
|
SfComponentType
|
Type of component. |
required |
name
|
str
|
Component registry name (case-insensitive). |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Schema dict with keys: |
dict[str, Any]
|
|
dict[str, Any]
|
|
Raises:
| Type | Description |
|---|---|
KeyError
|
If component not found. |
Example
schema = registry.get_schema(SfComponentType.DETECTOR, "example/sma_cross") print(schema["description"]) # Short summary print(schema["docstring"]) # Full docstring for UI for p in schema["parameters"]: ... print(f"{p['name']}: {p['type']} = {p['default']}")
Source code in src/signalflow/core/registry.py
list ¶
List registered components for a type.
Returns sorted list of component names for given type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component_type
|
SfComponentType
|
Type of components to list. |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
list[str]: Sorted list of registered component names. |
Example
# List all detectors
detectors = registry.list(SfComponentType.DETECTOR)
print(f"Available detectors: {detectors}")
# Output: ['ema_cross', 'macd', 'rsi_threshold', 'sma_cross']
# Check if component exists
if "sma_cross" in registry.list(SfComponentType.DETECTOR):
detector = registry.create(SfComponentType.DETECTOR, "sma_cross")
# List all component types
from signalflow.core.enums import SfComponentType
for component_type in SfComponentType:
components = registry.list(component_type)
print(f"{component_type.value}: {components}")
Source code in src/signalflow/core/registry.py
list_raw_data_types ¶
List all registered raw data type names.
Returns:
| Type | Description |
|---|---|
list[str]
|
Sorted list of registered data type names. |
register ¶
register(component_type: SfComponentType, name: str, cls: type[Any], *, override: bool = False) -> None
Register a class under (component_type, name).
Stores class with metadata (docstring, module) in registry for later lookup, instantiation, and UI display. Names are normalized to lowercase for case-insensitive lookup.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component_type
|
SfComponentType
|
Type of component (DETECTOR, EXTRACTOR, etc.). |
required |
name
|
str
|
Registry name (case-insensitive, will be lowercased). |
required |
cls
|
Type[Any]
|
Class to register. |
required |
override
|
bool
|
Allow overriding existing registration. Default: False. |
False
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If name is empty or already registered (when override=False). |
Example
# Register new component
registry.register(
SfComponentType.DETECTOR,
name="my_detector",
cls=MyDetector
)
# Override existing component
registry.register(
SfComponentType.DETECTOR,
name="my_detector",
cls=ImprovedDetector,
override=True # Logs warning
)
# Register multiple types
registry.register(SfComponentType.EXTRACTOR, "rsi", RsiExtractor)
registry.register(SfComponentType.LABELER, "fixed", FixedHorizonLabeler)
Source code in src/signalflow/core/registry.py
register_raw_data_type ¶
Register a custom raw data type with its required columns.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Data type identifier (case-insensitive, stored lowercase). |
required |
columns
|
list[str] | set[str]
|
Required column names for this data type. |
required |
override
|
bool
|
Allow overriding an existing registration. |
False
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If name is empty, columns are empty, or name already registered (when override=False). |
Example
Source code in src/signalflow/core/registry.py
snapshot ¶
Snapshot of registry for debugging.
Returns complete registry state organized by component type.
Returns:
| Type | Description |
|---|---|
dict[str, list[str]]
|
dict[str, list[str]]: Dictionary mapping component type names to sorted lists of registered component names. |
Example
# Get full registry snapshot
snapshot = registry.snapshot()
print(snapshot)
# Output:
# {
# 'DETECTOR': ['ema_cross', 'sma_cross'],
# 'EXTRACTOR': ['rsi', 'sma'],
# 'LABELER': ['fixed', 'triple_barrier'],
# 'ENTRY_RULE': ['fixed_size'],
# 'EXIT_RULE': ['take_profit', 'time_based']
# }
# Use for debugging
import json
print(json.dumps(registry.snapshot(), indent=2))
# Check registration status
snapshot = registry.snapshot()
if 'DETECTOR' in snapshot and 'sma_cross' in snapshot['DETECTOR']:
print("SMA detector is registered")