signalflow-trading 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. signalflow/__init__.py +21 -0
  2. signalflow/analytics/__init__.py +0 -0
  3. signalflow/core/__init__.py +46 -0
  4. signalflow/core/base_mixin.py +232 -0
  5. signalflow/core/containers/__init__.py +21 -0
  6. signalflow/core/containers/order.py +216 -0
  7. signalflow/core/containers/portfolio.py +211 -0
  8. signalflow/core/containers/position.py +296 -0
  9. signalflow/core/containers/raw_data.py +167 -0
  10. signalflow/core/containers/raw_data_view.py +169 -0
  11. signalflow/core/containers/signals.py +198 -0
  12. signalflow/core/containers/strategy_state.py +147 -0
  13. signalflow/core/containers/trade.py +112 -0
  14. signalflow/core/decorators.py +103 -0
  15. signalflow/core/enums.py +270 -0
  16. signalflow/core/registry.py +322 -0
  17. signalflow/core/rolling_aggregator.py +362 -0
  18. signalflow/core/signal_transforms/__init__.py +5 -0
  19. signalflow/core/signal_transforms/base_signal_transform.py +186 -0
  20. signalflow/data/__init__.py +11 -0
  21. signalflow/data/raw_data_factory.py +225 -0
  22. signalflow/data/raw_store/__init__.py +7 -0
  23. signalflow/data/raw_store/base.py +271 -0
  24. signalflow/data/raw_store/duckdb_stores.py +696 -0
  25. signalflow/data/source/__init__.py +10 -0
  26. signalflow/data/source/base.py +300 -0
  27. signalflow/data/source/binance.py +442 -0
  28. signalflow/data/strategy_store/__init__.py +8 -0
  29. signalflow/data/strategy_store/base.py +278 -0
  30. signalflow/data/strategy_store/duckdb.py +409 -0
  31. signalflow/data/strategy_store/schema.py +36 -0
  32. signalflow/detector/__init__.py +7 -0
  33. signalflow/detector/adapter/__init__.py +5 -0
  34. signalflow/detector/adapter/pandas_detector.py +46 -0
  35. signalflow/detector/base.py +390 -0
  36. signalflow/detector/sma_cross.py +105 -0
  37. signalflow/feature/__init__.py +16 -0
  38. signalflow/feature/adapter/__init__.py +5 -0
  39. signalflow/feature/adapter/pandas_feature_extractor.py +54 -0
  40. signalflow/feature/base.py +330 -0
  41. signalflow/feature/feature_set.py +286 -0
  42. signalflow/feature/oscillator/__init__.py +5 -0
  43. signalflow/feature/oscillator/rsi_extractor.py +42 -0
  44. signalflow/feature/pandasta/__init__.py +10 -0
  45. signalflow/feature/pandasta/pandas_ta_extractor.py +141 -0
  46. signalflow/feature/pandasta/top_pandasta_extractors.py +64 -0
  47. signalflow/feature/smoother/__init__.py +5 -0
  48. signalflow/feature/smoother/sma_extractor.py +46 -0
  49. signalflow/strategy/__init__.py +9 -0
  50. signalflow/strategy/broker/__init__.py +15 -0
  51. signalflow/strategy/broker/backtest.py +172 -0
  52. signalflow/strategy/broker/base.py +186 -0
  53. signalflow/strategy/broker/executor/__init__.py +9 -0
  54. signalflow/strategy/broker/executor/base.py +35 -0
  55. signalflow/strategy/broker/executor/binance_spot.py +12 -0
  56. signalflow/strategy/broker/executor/virtual_spot.py +81 -0
  57. signalflow/strategy/broker/realtime_spot.py +12 -0
  58. signalflow/strategy/component/__init__.py +9 -0
  59. signalflow/strategy/component/base.py +65 -0
  60. signalflow/strategy/component/entry/__init__.py +7 -0
  61. signalflow/strategy/component/entry/fixed_size.py +57 -0
  62. signalflow/strategy/component/entry/signal.py +127 -0
  63. signalflow/strategy/component/exit/__init__.py +5 -0
  64. signalflow/strategy/component/exit/time_based.py +47 -0
  65. signalflow/strategy/component/exit/tp_sl.py +80 -0
  66. signalflow/strategy/component/metric/__init__.py +8 -0
  67. signalflow/strategy/component/metric/main_metrics.py +181 -0
  68. signalflow/strategy/runner/__init__.py +8 -0
  69. signalflow/strategy/runner/backtest_runner.py +208 -0
  70. signalflow/strategy/runner/base.py +19 -0
  71. signalflow/strategy/runner/optimized_backtest_runner.py +178 -0
  72. signalflow/strategy/runner/realtime_runner.py +0 -0
  73. signalflow/target/__init__.py +14 -0
  74. signalflow/target/adapter/__init__.py +5 -0
  75. signalflow/target/adapter/pandas_labeler.py +45 -0
  76. signalflow/target/base.py +409 -0
  77. signalflow/target/fixed_horizon_labeler.py +93 -0
  78. signalflow/target/static_triple_barrier.py +162 -0
  79. signalflow/target/triple_barrier.py +188 -0
  80. signalflow/utils/__init__.py +7 -0
  81. signalflow/utils/import_utils.py +11 -0
  82. signalflow/utils/tune_utils.py +19 -0
  83. signalflow/validator/__init__.py +6 -0
  84. signalflow/validator/base.py +139 -0
  85. signalflow/validator/sklearn_validator.py +527 -0
  86. signalflow_trading-0.2.1.dist-info/METADATA +149 -0
  87. signalflow_trading-0.2.1.dist-info/RECORD +90 -0
  88. signalflow_trading-0.2.1.dist-info/WHEEL +5 -0
  89. signalflow_trading-0.2.1.dist-info/licenses/LICENSE +21 -0
  90. signalflow_trading-0.2.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,103 @@
1
+ from typing import Any, Type
2
+ from signalflow.core.registry import default_registry
3
+ from signalflow.core.enums import SfComponentType
4
+
5
+
6
+ def sf_component(*, name: str, override: bool = False):
7
+ """Register class as SignalFlow component.
8
+
9
+ Decorator that registers a class in the global component registry,
10
+ making it discoverable by name for dynamic instantiation.
11
+
12
+ The decorated class must have a `component_type` class attribute
13
+ of type `SfComponentType` to indicate what kind of component it is
14
+ (e.g., DETECTOR, EXTRACTOR, LABELER, ENTRY_RULE, EXIT_RULE).
15
+
16
+ Args:
17
+ name (str): Registry name for the component (case-insensitive).
18
+ override (bool): Allow overriding existing registration. Default: False.
19
+
20
+ Returns:
21
+ Callable: Decorator function that registers and returns the class unchanged.
22
+
23
+ Raises:
24
+ ValueError: If class doesn't define component_type attribute.
25
+ ValueError: If name already registered and override=False.
26
+
27
+ Example:
28
+ ```python
29
+ from signalflow.core import sf_component
30
+ from signalflow.core.enums import SfComponentType
31
+ from signalflow.detector import SignalDetector
32
+
33
+ @sf_component(name="my_detector")
34
+ class MyDetector(SignalDetector):
35
+ component_type = SfComponentType.DETECTOR
36
+
37
+ def detect(self, df):
38
+ # Detection logic
39
+ return signals
40
+
41
+ # Later, instantiate by name
42
+ from signalflow.core.registry import default_registry
43
+
44
+ detector_cls = default_registry.get(
45
+ SfComponentType.DETECTOR,
46
+ "my_detector"
47
+ )
48
+ detector = detector_cls(params={"window": 20})
49
+
50
+ # Override existing registration
51
+ @sf_component(name="my_detector", override=True)
52
+ class ImprovedDetector(SignalDetector):
53
+ component_type = SfComponentType.DETECTOR
54
+ # ... improved implementation
55
+ ```
56
+
57
+ Example:
58
+ ```python
59
+ # Register multiple component types
60
+
61
+ @sf_component(name="sma_cross")
62
+ class SmaCrossDetector(SignalDetector):
63
+ component_type = SfComponentType.DETECTOR
64
+ # ...
65
+
66
+ @sf_component(name="rsi")
67
+ class RsiExtractor(FeatureExtractor):
68
+ component_type = SfComponentType.EXTRACTOR
69
+ # ...
70
+
71
+ @sf_component(name="fixed_size")
72
+ class FixedSizeEntry(SignalEntryRule):
73
+ component_type = SfComponentType.ENTRY_RULE
74
+ # ...
75
+
76
+ @sf_component(name="take_profit")
77
+ class TakeProfitExit(ExitRule):
78
+ component_type = SfComponentType.EXIT_RULE
79
+ # ...
80
+ ```
81
+
82
+ Note:
83
+ Component names are case-insensitive for lookup.
84
+ The class itself is not modified - only registered.
85
+ Use override=True carefully to avoid accidental overrides.
86
+ """
87
+ def decorator(cls: Type[Any]) -> Type[Any]:
88
+ component_type = getattr(cls, "component_type", None)
89
+ if not isinstance(component_type, SfComponentType):
90
+ raise ValueError(
91
+ f"{cls.__name__} must define class attribute "
92
+ f"'component_type: SfComponentType'"
93
+ )
94
+
95
+ default_registry.register(
96
+ component_type,
97
+ name=name,
98
+ cls=cls,
99
+ override=override,
100
+ )
101
+ return cls
102
+
103
+ return decorator
@@ -0,0 +1,270 @@
1
+ from enum import Enum
2
+
3
+
4
+ class SignalType(str, Enum):
5
+ """Enumeration of signal types.
6
+
7
+ Represents the direction of a trading signal detected by signal detectors.
8
+
9
+ Values:
10
+ NONE: No signal detected or neutral state.
11
+ RISE: Bullish signal indicating potential price increase.
12
+ FALL: Bearish signal indicating potential price decrease.
13
+
14
+ Example:
15
+ ```python
16
+ from signalflow.core.enums import SignalType
17
+
18
+ # Check signal type
19
+ if signal_type == SignalType.RISE:
20
+ print("Bullish signal detected")
21
+ elif signal_type == SignalType.FALL:
22
+ print("Bearish signal detected")
23
+ else:
24
+ print("No signal")
25
+
26
+ # Use in DataFrame
27
+ import polars as pl
28
+ signals_df = pl.DataFrame({
29
+ "pair": ["BTCUSDT"],
30
+ "timestamp": [datetime.now()],
31
+ "signal_type": [SignalType.RISE.value]
32
+ })
33
+
34
+ # Compare with enum
35
+ is_rise = signals_df.filter(
36
+ pl.col("signal_type") == SignalType.RISE.value
37
+ )
38
+ ```
39
+
40
+ Note:
41
+ Stored as string values in DataFrames for serialization.
42
+ Use .value to get string representation.
43
+ """
44
+ NONE = "none"
45
+ RISE = "rise"
46
+ FALL = "fall"
47
+
48
+
49
+ class PositionType(str, Enum):
50
+ """Enumeration of position types.
51
+
52
+ Represents the direction of a trading position.
53
+
54
+ Values:
55
+ LONG: Long position (profit from price increase).
56
+ SHORT: Short position (profit from price decrease).
57
+
58
+ Example:
59
+ ```python
60
+ from signalflow.core import Position, PositionType
61
+
62
+ # Create long position
63
+ long_position = Position(
64
+ pair="BTCUSDT",
65
+ position_type=PositionType.LONG,
66
+ entry_price=45000.0,
67
+ qty=0.5
68
+ )
69
+
70
+ # Check position type
71
+ if position.position_type == PositionType.LONG:
72
+ print("Long position")
73
+ assert position.side_sign == 1.0
74
+ else:
75
+ print("Short position")
76
+ assert position.side_sign == -1.0
77
+
78
+ # Store in DataFrame
79
+ positions_df = pl.DataFrame({
80
+ "pair": ["BTCUSDT"],
81
+ "position_type": [PositionType.LONG.value],
82
+ "qty": [0.5]
83
+ })
84
+ ```
85
+
86
+ Note:
87
+ Currently only LONG positions are fully implemented.
88
+ SHORT positions planned for future versions.
89
+ """
90
+ LONG = "long"
91
+ SHORT = "short"
92
+
93
+
94
+ class SfComponentType(str, Enum):
95
+ """Enumeration of SignalFlow component types.
96
+
97
+ Defines all component types that can be registered in the component registry.
98
+ Used by sf_component decorator and SignalFlowRegistry for type-safe registration.
99
+
100
+ Component categories:
101
+ - Data: Raw data loading and storage
102
+ - Feature: Feature extraction
103
+ - Signals: Signal detection, transformation, labeling, validation
104
+ - Strategy: Execution, rules, metrics
105
+
106
+ Values:
107
+ RAW_DATA_STORE: Raw data storage backends (e.g., DuckDB, Parquet).
108
+ RAW_DATA_SOURCE: Raw data sources (e.g., Binance API).
109
+ RAW_DATA_LOADER: Raw data loaders combining source + store.
110
+ FEATURE_EXTRACTOR: Feature extraction classes (e.g., RSI, SMA).
111
+ SIGNALS_TRANSFORM: Signal transformation functions.
112
+ LABELER: Signal labeling strategies (e.g., triple barrier).
113
+ DETECTOR: Signal detection algorithms (e.g., SMA cross).
114
+ VALIDATOR: Signal validation models.
115
+ TORCH_MODULE: PyTorch neural network modules.
116
+ VALIDATOR_MODEL: Pre-trained validator models.
117
+ STRATEGY_STORE: Strategy state persistence backends.
118
+ STRATEGY_RUNNER: Backtest/live runner implementations.
119
+ STRATEGY_BROKER: Order management and position tracking.
120
+ STRATEGY_EXECUTOR: Order execution engines (backtest/live).
121
+ STRATEGY_EXIT_RULE: Position exit rules (e.g., take profit, stop loss).
122
+ STRATEGY_ENTRY_RULE: Position entry rules (e.g., fixed size).
123
+ STRATEGY_METRIC: Strategy performance metrics.
124
+
125
+ Example:
126
+ ```python
127
+ from signalflow.core import sf_component
128
+ from signalflow.core.enums import SfComponentType
129
+ from signalflow.detector import SignalDetector
130
+
131
+ # Register detector
132
+ @sf_component(name="my_detector")
133
+ class MyDetector(SignalDetector):
134
+ component_type = SfComponentType.DETECTOR
135
+ # ... implementation
136
+
137
+ # Register extractor
138
+ @sf_component(name="my_feature")
139
+ class MyExtractor(FeatureExtractor):
140
+ component_type = SfComponentType.FEATURE_EXTRACTOR
141
+ # ... implementation
142
+
143
+ # Register exit rule
144
+ @sf_component(name="my_exit")
145
+ class MyExit(ExitRule):
146
+ component_type = SfComponentType.STRATEGY_EXIT_RULE
147
+ # ... implementation
148
+
149
+ # Use in registry
150
+ from signalflow.core.registry import default_registry
151
+
152
+ detector = default_registry.create(
153
+ SfComponentType.DETECTOR,
154
+ "my_detector"
155
+ )
156
+ ```
157
+
158
+ Note:
159
+ All registered components must have component_type class attribute.
160
+ Component types are organized hierarchically (category/subcategory).
161
+ """
162
+ RAW_DATA_STORE = "data/store"
163
+ RAW_DATA_SOURCE = "data/source"
164
+ RAW_DATA_LOADER = "data/loader"
165
+
166
+ FEATURE_EXTRACTOR = "feature/extractor"
167
+
168
+ SIGNALS_TRANSFORM = "signals/transform"
169
+ LABELER = "signals/labeler"
170
+ DETECTOR = "signals/detector"
171
+ VALIDATOR = "signals/validator"
172
+ TORCH_MODULE = "torch_module"
173
+ VALIDATOR_MODEL = "signals/validator/model"
174
+
175
+ STRATEGY_STORE = "strategy/store"
176
+ STRATEGY_RUNNER = "strategy/runner"
177
+ STRATEGY_BROKER = "strategy/broker"
178
+ STRATEGY_EXECUTOR = "strategy/executor"
179
+ STRATEGY_EXIT_RULE = "strategy/exit"
180
+ STRATEGY_ENTRY_RULE = "strategy/entry"
181
+ STRATEGY_METRIC = "strategy/metric"
182
+
183
+
184
+ class DataFrameType(str, Enum):
185
+ """Supported DataFrame backends.
186
+
187
+ Specifies which DataFrame library to use for data processing.
188
+ Used by FeatureExtractor and other components to determine input/output format.
189
+
190
+ Values:
191
+ POLARS: Polars DataFrame (faster, modern).
192
+ PANDAS: Pandas DataFrame (legacy compatibility).
193
+
194
+ Example:
195
+ ```python
196
+ from signalflow.core.enums import DataFrameType
197
+ from signalflow.feature import FeatureExtractor
198
+
199
+ # Polars-based extractor
200
+ class MyExtractor(FeatureExtractor):
201
+ df_type = DataFrameType.POLARS
202
+
203
+ def extract(self, df: pl.DataFrame) -> pl.DataFrame:
204
+ return df.with_columns(
205
+ pl.col("close").rolling_mean(20).alias("sma_20")
206
+ )
207
+
208
+ # Pandas-based extractor
209
+ class LegacyExtractor(FeatureExtractor):
210
+ df_type = DataFrameType.PANDAS
211
+
212
+ def extract(self, df: pd.DataFrame) -> pd.DataFrame:
213
+ df["sma_20"] = df["close"].rolling(20).mean()
214
+ return df
215
+
216
+ # Use in RawDataView
217
+ from signalflow.core import RawDataView
218
+
219
+ view = RawDataView(raw=raw_data)
220
+
221
+ # Get data in required format
222
+ df_polars = view.get_data("spot", DataFrameType.POLARS)
223
+ df_pandas = view.get_data("spot", DataFrameType.PANDAS)
224
+ ```
225
+
226
+ Note:
227
+ New code should prefer POLARS for better performance.
228
+ PANDAS supported for backward compatibility and legacy libraries.
229
+ """
230
+ POLARS = "polars"
231
+ PANDAS = "pandas"
232
+
233
+ class RawDataType(str, Enum):
234
+ """Supported raw data types.
235
+
236
+ Defines types of market data that can be loaded and processed.
237
+
238
+ Values:
239
+ SPOT: Spot trading data (OHLCV).
240
+
241
+ Example:
242
+ ```python
243
+ from signalflow.core.enums import RawDataType
244
+
245
+ # Load spot data
246
+ loader = BinanceLoader(
247
+ pairs=["BTCUSDT", "ETHUSDT"],
248
+ data_type=RawDataType.SPOT
249
+ )
250
+
251
+ raw_data = loader.load(
252
+ datetime_start=datetime(2024, 1, 1),
253
+ datetime_end=datetime(2024, 12, 31)
254
+ )
255
+
256
+ # Access spot data
257
+ spot_df = raw_data[RawDataType.SPOT.value]
258
+
259
+ # Check data type
260
+ if raw_data_type == RawDataType.SPOT:
261
+ print("Processing spot data")
262
+ ```
263
+
264
+ Note:
265
+ Future versions will add:
266
+ - FUTURES: Futures trading data
267
+ - PERPETUAL: Perpetual swaps data
268
+ - LOB: Limit order book data
269
+ """
270
+ SPOT = "spot"
@@ -0,0 +1,322 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Dict, Type
5
+ from loguru import logger
6
+ from .enums import SfComponentType
7
+
8
+
9
+ @dataclass
10
+ class SignalFlowRegistry:
11
+ """Component registry for dynamic component discovery and instantiation.
12
+
13
+ Provides centralized registration and lookup for SignalFlow components.
14
+ Components are organized by type (DETECTOR, EXTRACTOR, etc.) and
15
+ accessed by case-insensitive names.
16
+
17
+ Registry structure:
18
+ component_type -> name -> class
19
+
20
+ Supported component types:
21
+ - DETECTOR: Signal detection classes
22
+ - EXTRACTOR: Feature extraction classes
23
+ - LABELER: Signal labeling classes
24
+ - ENTRY_RULE: Position entry rules
25
+ - EXIT_RULE: Position exit rules
26
+ - METRIC: Strategy metrics
27
+ - EXECUTOR: Order execution engines
28
+
29
+ Attributes:
30
+ _items (dict[SfComponentType, dict[str, Type[Any]]]):
31
+ Internal storage mapping component types to name-class pairs.
32
+
33
+ Example:
34
+ ```python
35
+ from signalflow.core.registry import SignalFlowRegistry
36
+ from signalflow.core.enums import SfComponentType
37
+
38
+ # Create registry
39
+ registry = SignalFlowRegistry()
40
+
41
+ # Register component
42
+ registry.register(
43
+ SfComponentType.DETECTOR,
44
+ name="sma_cross",
45
+ cls=SmaCrossDetector
46
+ )
47
+
48
+ # Get component class
49
+ detector_cls = registry.get(SfComponentType.DETECTOR, "sma_cross")
50
+
51
+ # Instantiate component
52
+ detector = registry.create(
53
+ SfComponentType.DETECTOR,
54
+ "sma_cross",
55
+ fast_window=10,
56
+ slow_window=20
57
+ )
58
+
59
+ # List available components
60
+ detectors = registry.list(SfComponentType.DETECTOR)
61
+ print(f"Available detectors: {detectors}")
62
+
63
+ # Full snapshot
64
+ snapshot = registry.snapshot()
65
+ print(snapshot)
66
+ ```
67
+
68
+ Note:
69
+ Component names are stored and looked up in lowercase.
70
+ Use default_registry singleton for application-wide registration.
71
+
72
+ See Also:
73
+ sf_component: Decorator for automatic component registration.
74
+ """
75
+ #TODO: Registry autodiscover
76
+
77
+ _items: Dict[SfComponentType, Dict[str, Type[Any]]] = field(default_factory=dict)
78
+
79
+ def _ensure(self, component_type: SfComponentType) -> None:
80
+ """Ensure component type exists in registry.
81
+
82
+ Initializes empty dict for component_type if not present.
83
+
84
+ Args:
85
+ component_type (SfComponentType): Component type to ensure.
86
+ """
87
+ self._items.setdefault(component_type, {})
88
+
89
+ def register(self, component_type: SfComponentType, name: str, cls: Type[Any], *, override: bool = False) -> None:
90
+ """Register a class under (component_type, name).
91
+
92
+ Stores class in registry for later lookup and instantiation.
93
+ Names are normalized to lowercase for case-insensitive lookup.
94
+
95
+ Args:
96
+ component_type (SfComponentType): Type of component (DETECTOR, EXTRACTOR, etc.).
97
+ name (str): Registry name (case-insensitive, will be lowercased).
98
+ cls (Type[Any]): Class to register.
99
+ override (bool): Allow overriding existing registration. Default: False.
100
+
101
+ Raises:
102
+ ValueError: If name is empty or already registered (when override=False).
103
+
104
+ Example:
105
+ ```python
106
+ # Register new component
107
+ registry.register(
108
+ SfComponentType.DETECTOR,
109
+ name="my_detector",
110
+ cls=MyDetector
111
+ )
112
+
113
+ # Override existing component
114
+ registry.register(
115
+ SfComponentType.DETECTOR,
116
+ name="my_detector",
117
+ cls=ImprovedDetector,
118
+ override=True # Logs warning
119
+ )
120
+
121
+ # Register multiple types
122
+ registry.register(SfComponentType.EXTRACTOR, "rsi", RsiExtractor)
123
+ registry.register(SfComponentType.LABELER, "fixed", FixedHorizonLabeler)
124
+ ```
125
+ """
126
+ if not isinstance(name, str) or not name.strip():
127
+ raise ValueError("name must be a non-empty string")
128
+
129
+ key = name.strip().lower()
130
+ self._ensure(component_type)
131
+
132
+ if key in self._items[component_type] and not override:
133
+ raise ValueError(f"{component_type.value}:{key} already registered")
134
+
135
+ if key in self._items[component_type] and override:
136
+ logger.warning(f"Overriding {component_type.value}:{key} with {cls.__name__}")
137
+
138
+ self._items[component_type][key] = cls
139
+
140
+ def get(self, component_type: SfComponentType, name: str) -> Type[Any]:
141
+ """Get a registered class by key.
142
+
143
+ Lookup is case-insensitive. Raises helpful error with available
144
+ components if key not found.
145
+
146
+ Args:
147
+ component_type (SfComponentType): Type of component to lookup.
148
+ name (str): Component name (case-insensitive).
149
+
150
+ Returns:
151
+ Type[Any]: Registered class.
152
+
153
+ Raises:
154
+ KeyError: If component not found. Error message includes available components.
155
+
156
+ Example:
157
+ ```python
158
+ # Get component class
159
+ detector_cls = registry.get(SfComponentType.DETECTOR, "sma_cross")
160
+
161
+ # Case-insensitive
162
+ detector_cls = registry.get(SfComponentType.DETECTOR, "SMA_Cross")
163
+
164
+ # Instantiate manually
165
+ detector = detector_cls(fast_window=10, slow_window=20)
166
+
167
+ # Handle missing component
168
+ try:
169
+ cls = registry.get(SfComponentType.DETECTOR, "unknown")
170
+ except KeyError as e:
171
+ print(f"Component not found: {e}")
172
+ # Shows: "Component not found: DETECTOR:unknown. Available: [sma_cross, ...]"
173
+ ```
174
+ """
175
+ self._ensure(component_type)
176
+ key = name.lower()
177
+ try:
178
+ return self._items[component_type][key]
179
+ except KeyError as e:
180
+ available = ", ".join(sorted(self._items[component_type]))
181
+ raise KeyError(
182
+ f"Component not found: {component_type.value}:{key}. Available: [{available}]"
183
+ ) from e
184
+
185
+ def create(self, component_type: SfComponentType, name: str, **kwargs: Any) -> Any:
186
+ """Instantiate a component by registry key.
187
+
188
+ Convenient method that combines get() and instantiation.
189
+
190
+ Args:
191
+ component_type (SfComponentType): Type of component to create.
192
+ name (str): Component name (case-insensitive).
193
+ **kwargs: Arguments to pass to component constructor.
194
+
195
+ Returns:
196
+ Any: Instantiated component.
197
+
198
+ Raises:
199
+ KeyError: If component not found.
200
+ TypeError: If kwargs don't match component constructor.
201
+
202
+ Example:
203
+ ```python
204
+ # Create detector with params
205
+ detector = registry.create(
206
+ SfComponentType.DETECTOR,
207
+ "sma_cross",
208
+ fast_window=10,
209
+ slow_window=20
210
+ )
211
+
212
+ # Create extractor
213
+ extractor = registry.create(
214
+ SfComponentType.EXTRACTOR,
215
+ "rsi",
216
+ window=14
217
+ )
218
+
219
+ # Create with config dict
220
+ config = {"window": 20, "threshold": 0.7}
221
+ labeler = registry.create(
222
+ SfComponentType.LABELER,
223
+ "fixed",
224
+ **config
225
+ )
226
+ ```
227
+ """
228
+ cls = self.get(component_type, name)
229
+ return cls(**kwargs)
230
+
231
+ def list(self, component_type: SfComponentType) -> list[str]:
232
+ """List registered components for a type.
233
+
234
+ Returns sorted list of component names for given type.
235
+
236
+ Args:
237
+ component_type (SfComponentType): Type of components to list.
238
+
239
+ Returns:
240
+ list[str]: Sorted list of registered component names.
241
+
242
+ Example:
243
+ ```python
244
+ # List all detectors
245
+ detectors = registry.list(SfComponentType.DETECTOR)
246
+ print(f"Available detectors: {detectors}")
247
+ # Output: ['ema_cross', 'macd', 'rsi_threshold', 'sma_cross']
248
+
249
+ # Check if component exists
250
+ if "sma_cross" in registry.list(SfComponentType.DETECTOR):
251
+ detector = registry.create(SfComponentType.DETECTOR, "sma_cross")
252
+
253
+ # List all component types
254
+ from signalflow.core.enums import SfComponentType
255
+ for component_type in SfComponentType:
256
+ components = registry.list(component_type)
257
+ print(f"{component_type.value}: {components}")
258
+ ```
259
+ """
260
+ self._ensure(component_type)
261
+ return sorted(self._items[component_type])
262
+
263
+ def snapshot(self) -> dict[str, list[str]]:
264
+ """Snapshot of registry for debugging.
265
+
266
+ Returns complete registry state organized by component type.
267
+
268
+ Returns:
269
+ dict[str, list[str]]: Dictionary mapping component type names
270
+ to sorted lists of registered component names.
271
+
272
+ Example:
273
+ ```python
274
+ # Get full registry snapshot
275
+ snapshot = registry.snapshot()
276
+ print(snapshot)
277
+ # Output:
278
+ # {
279
+ # 'DETECTOR': ['ema_cross', 'sma_cross'],
280
+ # 'EXTRACTOR': ['rsi', 'sma'],
281
+ # 'LABELER': ['fixed', 'triple_barrier'],
282
+ # 'ENTRY_RULE': ['fixed_size'],
283
+ # 'EXIT_RULE': ['take_profit', 'time_based']
284
+ # }
285
+
286
+ # Use for debugging
287
+ import json
288
+ print(json.dumps(registry.snapshot(), indent=2))
289
+
290
+ # Check registration status
291
+ snapshot = registry.snapshot()
292
+ if 'DETECTOR' in snapshot and 'sma_cross' in snapshot['DETECTOR']:
293
+ print("SMA detector is registered")
294
+ ```
295
+ """
296
+ return {t.value: sorted(v.keys()) for t, v in self._items.items()}
297
+
298
+
299
+ default_registry = SignalFlowRegistry()
300
+ """Global default registry instance.
301
+
302
+ Use this singleton for application-wide component registration.
303
+
304
+ Example:
305
+ ```python
306
+ from signalflow.core.registry import default_registry
307
+ from signalflow.core.enums import SfComponentType
308
+
309
+ # Register to default registry
310
+ default_registry.register(
311
+ SfComponentType.DETECTOR,
312
+ "my_detector",
313
+ MyDetector
314
+ )
315
+
316
+ # Access from anywhere
317
+ detector = default_registry.create(
318
+ SfComponentType.DETECTOR,
319
+ "my_detector"
320
+ )
321
+ ```
322
+ """