processbehavior 0.2.0__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.
- processbehavior/__init__.py +133 -0
- processbehavior/analysis.py +2944 -0
- processbehavior/analysis_dataset.py +361 -0
- processbehavior/analysis_result.py +1276 -0
- processbehavior/calibration.py +70 -0
- processbehavior/capability.py +720 -0
- processbehavior/data_preparation.py +792 -0
- processbehavior/datasets/__init__.py +37 -0
- processbehavior/datasets/data/coffee_shop_demo_long.csv +385 -0
- processbehavior/datasets/loaders.py +66 -0
- processbehavior/datasets/synthetic.py +1711 -0
- processbehavior/derivations.py +602 -0
- processbehavior/effects_calculator.py +603 -0
- processbehavior/exceptions.py +216 -0
- processbehavior/exporters/__init__.py +5 -0
- processbehavior/exporters/excel_exporter.py +708 -0
- processbehavior/focused_result.py +329 -0
- processbehavior/formulation_spec.py +144 -0
- processbehavior/loss_function.py +517 -0
- processbehavior/maximum_information.py +285 -0
- processbehavior/plotting/__init__.py +46 -0
- processbehavior/plotting/_math.py +121 -0
- processbehavior/plotting/capability_chart.py +858 -0
- processbehavior/plotting/contracts.py +176 -0
- processbehavior/plotting/control_chart.py +206 -0
- processbehavior/plotting/effects_charts.py +640 -0
- processbehavior/plotting/lane_boundaries.py +78 -0
- processbehavior/plotting/limits.py +157 -0
- processbehavior/plotting/maximum_information_chart.py +395 -0
- processbehavior/plotting/plotter.py +1469 -0
- processbehavior/plotting/renderers.py +660 -0
- processbehavior/plotting/report.py +211 -0
- processbehavior/plotting/residuals.py +282 -0
- processbehavior/plotting/run_rules_viz.py +150 -0
- processbehavior/plotting/stats_box.py +183 -0
- processbehavior/plotting/themes.py +544 -0
- processbehavior/plotting/x_axis_layout.py +474 -0
- processbehavior/plotting/zones.py +127 -0
- processbehavior/process_behavior.py +1290 -0
- processbehavior/py.typed +0 -0
- processbehavior/residual_calculator.py +572 -0
- processbehavior/result_signals.py +149 -0
- processbehavior/result_tabular.py +296 -0
- processbehavior/sds_detector.py +1432 -0
- processbehavior/signals/__init__.py +14 -0
- processbehavior/signals/config.py +435 -0
- processbehavior/signals/detector.py +328 -0
- processbehavior/signals/detectors.py +368 -0
- processbehavior/signals/result.py +232 -0
- processbehavior/spc_constants.py +692 -0
- processbehavior/study.py +2733 -0
- processbehavior/types.py +154 -0
- processbehavior-0.2.0.dist-info/METADATA +279 -0
- processbehavior-0.2.0.dist-info/RECORD +56 -0
- processbehavior-0.2.0.dist-info/WHEEL +4 -0
- processbehavior-0.2.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Signal detection framework for control charts.
|
|
3
|
+
|
|
4
|
+
Provides flexible, declarative API for detecting Western Electric rules
|
|
5
|
+
and custom patterns in process behavior data.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from .config import RuleSet, SignalConfig, ZoneDefinition
|
|
11
|
+
from .detector import SignalDetector
|
|
12
|
+
from .result import SignalResult
|
|
13
|
+
|
|
14
|
+
__all__ = ['SignalConfig', 'ZoneDefinition', 'RuleSet', 'SignalDetector', 'SignalResult']
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration classes for signal detection.
|
|
3
|
+
|
|
4
|
+
Provides flexible configuration through dataclasses and fluent API.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import Callable
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from typing import Literal
|
|
12
|
+
|
|
13
|
+
from ..exceptions import ValidationError
|
|
14
|
+
|
|
15
|
+
# Chart-type-based rule defaults
|
|
16
|
+
# ================================
|
|
17
|
+
# WECO rules 2-8 require sequential/temporal ordering of observations.
|
|
18
|
+
# - Xbar/S: Categorical comparison of subgroups → Only Rule 1 applies
|
|
19
|
+
# - X/mR: Sequential observations over time → All rules apply
|
|
20
|
+
# Easy to extend for new chart types (CUSUM, EWMA, etc.)
|
|
21
|
+
CHART_TYPE_RULES = {
|
|
22
|
+
'Xbar': ['rule_1'], # Rational subgroup means (categorical)
|
|
23
|
+
'S': ['rule_1'], # Rational subgroup variation (categorical)
|
|
24
|
+
'X': ['rule_1', 'rule_2', 'rule_3', 'rule_4', 'rule_5', 'rule_6', 'rule_7', 'rule_8'],
|
|
25
|
+
'mR': ['rule_1', 'rule_2', 'rule_3', 'rule_4', 'rule_5', 'rule_6', 'rule_7', 'rule_8'],
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class ZoneDefinition:
|
|
31
|
+
"""
|
|
32
|
+
Zone boundary definition in standard deviations from centerline.
|
|
33
|
+
|
|
34
|
+
Zones are defined as multiples of sigma (standard deviation):
|
|
35
|
+
- Zone A: 2σ to 3σ from centerline
|
|
36
|
+
- Zone B: 1σ to 2σ from centerline
|
|
37
|
+
- Zone C: 0σ to 1σ from centerline
|
|
38
|
+
|
|
39
|
+
Examples
|
|
40
|
+
--------
|
|
41
|
+
Default zones:
|
|
42
|
+
|
|
43
|
+
>>> zones = ZoneDefinition()
|
|
44
|
+
>>> zones.A # (2.0, 3.0)
|
|
45
|
+
|
|
46
|
+
Custom zones:
|
|
47
|
+
|
|
48
|
+
>>> zones = ZoneDefinition(A=(2.5, 3.5), B=(1.5, 2.5))
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
A: tuple[float, float] = (2.0, 3.0)
|
|
52
|
+
B: tuple[float, float] = (1.0, 2.0)
|
|
53
|
+
C: tuple[float, float] = (0.0, 1.0)
|
|
54
|
+
|
|
55
|
+
def get_boundaries(self, center: float, sigma: float) -> dict:
|
|
56
|
+
"""
|
|
57
|
+
Calculate actual zone boundaries for given center and sigma.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
center : float
|
|
62
|
+
Centerline value
|
|
63
|
+
sigma : float
|
|
64
|
+
Standard deviation
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
dict
|
|
69
|
+
Zone boundaries with keys like 'A_upper', 'A_lower', etc.
|
|
70
|
+
"""
|
|
71
|
+
return {
|
|
72
|
+
'A_upper': (center + self.A[0] * sigma, center + self.A[1] * sigma),
|
|
73
|
+
'A_lower': (center - self.A[1] * sigma, center - self.A[0] * sigma),
|
|
74
|
+
'B_upper': (center + self.B[0] * sigma, center + self.B[1] * sigma),
|
|
75
|
+
'B_lower': (center - self.B[1] * sigma, center - self.B[0] * sigma),
|
|
76
|
+
'C_upper': (center + self.C[0] * sigma, center + self.C[1] * sigma),
|
|
77
|
+
'C_lower': (center - self.C[1] * sigma, center - self.C[0] * sigma),
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass
|
|
82
|
+
class SignalConfig:
|
|
83
|
+
"""
|
|
84
|
+
Configuration for signal detection.
|
|
85
|
+
|
|
86
|
+
Provides flexible control over which rules to apply and how to apply them.
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
enabled_rules : list or str, default 'default'
|
|
91
|
+
Which rules to apply:
|
|
92
|
+
- 'default': Use chart-type-based defaults (Xbar/S: Rule 1, X/mR: Rules 1-8)
|
|
93
|
+
- 'standard': Rules 1-4 (filtered by chart type)
|
|
94
|
+
- 'extended': Rules 1-8 (filtered by chart type)
|
|
95
|
+
- 'all': Same as extended
|
|
96
|
+
- List of rule names: ['rule_1', 'rule_2', ...] (explicit override)
|
|
97
|
+
zone_definition : ZoneDefinition, optional
|
|
98
|
+
Custom zone definitions
|
|
99
|
+
min_observations : int, default 20
|
|
100
|
+
Minimum observations required
|
|
101
|
+
ignore_first_n : int, default 0
|
|
102
|
+
Ignore first N observations (startup period)
|
|
103
|
+
ignore_last_n : int, default 0
|
|
104
|
+
Ignore last N observations
|
|
105
|
+
use_vectorized : bool, default True
|
|
106
|
+
Use vectorized operations (faster)
|
|
107
|
+
|
|
108
|
+
Examples
|
|
109
|
+
--------
|
|
110
|
+
Standard configuration:
|
|
111
|
+
|
|
112
|
+
>>> config = SignalConfig(enabled_rules='standard')
|
|
113
|
+
|
|
114
|
+
Custom configuration:
|
|
115
|
+
|
|
116
|
+
>>> config = SignalConfig(
|
|
117
|
+
... enabled_rules=['rule_1', 'rule_2', 'rule_5'],
|
|
118
|
+
... zone_definition=ZoneDefinition(A=(2.5, 3.5)),
|
|
119
|
+
... min_observations=30
|
|
120
|
+
... )
|
|
121
|
+
|
|
122
|
+
Extended rules:
|
|
123
|
+
|
|
124
|
+
>>> config = SignalConfig(enabled_rules='extended')
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
enabled_rules: list[str] | Literal['default', 'standard', 'extended', 'all'] = 'default'
|
|
128
|
+
zone_definition: ZoneDefinition = field(default_factory=ZoneDefinition)
|
|
129
|
+
min_observations: int = 20
|
|
130
|
+
ignore_first_n: int = 0
|
|
131
|
+
ignore_last_n: int = 0
|
|
132
|
+
use_vectorized: bool = True
|
|
133
|
+
|
|
134
|
+
def __post_init__(self):
|
|
135
|
+
"""Resolve rule presets to actual rule lists."""
|
|
136
|
+
if isinstance(self.enabled_rules, str) and self.enabled_rules != 'default':
|
|
137
|
+
# Resolve presets except 'default' (resolved per chart type)
|
|
138
|
+
self.enabled_rules = self._resolve_preset(self.enabled_rules)
|
|
139
|
+
|
|
140
|
+
def _resolve_preset(self, preset: str) -> list[str]:
|
|
141
|
+
"""Convert preset name to rule list."""
|
|
142
|
+
presets = {
|
|
143
|
+
'standard': ['rule_1', 'rule_2', 'rule_3', 'rule_4'],
|
|
144
|
+
'extended': [f'rule_{i}' for i in range(1, 9)],
|
|
145
|
+
'all': [f'rule_{i}' for i in range(1, 9)],
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if preset not in presets:
|
|
149
|
+
raise ValidationError(f"Unknown preset '{preset}'.\\nValid presets: {list(presets.keys())}")
|
|
150
|
+
|
|
151
|
+
return presets[preset]
|
|
152
|
+
|
|
153
|
+
def get_rules_for_chart(self, chart_type: str) -> list[str]:
|
|
154
|
+
"""
|
|
155
|
+
Get applicable rules for a chart type.
|
|
156
|
+
|
|
157
|
+
Uses chart-type-based defaults or applies filtering to user-specified rules.
|
|
158
|
+
|
|
159
|
+
Parameters
|
|
160
|
+
----------
|
|
161
|
+
chart_type : str
|
|
162
|
+
Chart type ('Xbar', 'S', 'X', 'mR')
|
|
163
|
+
|
|
164
|
+
Returns
|
|
165
|
+
-------
|
|
166
|
+
list of str
|
|
167
|
+
Rule names to apply
|
|
168
|
+
|
|
169
|
+
Examples
|
|
170
|
+
--------
|
|
171
|
+
Use defaults:
|
|
172
|
+
|
|
173
|
+
>>> config = SignalConfig(enabled_rules='default')
|
|
174
|
+
>>> config.get_rules_for_chart('Xbar')
|
|
175
|
+
['rule_1']
|
|
176
|
+
>>> config.get_rules_for_chart('X')
|
|
177
|
+
['rule_1', 'rule_2', ..., 'rule_8']
|
|
178
|
+
|
|
179
|
+
Explicit rules (no filtering):
|
|
180
|
+
|
|
181
|
+
>>> config = SignalConfig(enabled_rules=['rule_1', 'rule_2'])
|
|
182
|
+
>>> config.get_rules_for_chart('Xbar')
|
|
183
|
+
['rule_1', 'rule_2']
|
|
184
|
+
"""
|
|
185
|
+
if isinstance(self.enabled_rules, list):
|
|
186
|
+
# User provided explicit list - use as-is
|
|
187
|
+
return self.enabled_rules
|
|
188
|
+
|
|
189
|
+
if self.enabled_rules == 'default':
|
|
190
|
+
# Use chart-type defaults
|
|
191
|
+
return CHART_TYPE_RULES.get(chart_type, ['rule_1'])
|
|
192
|
+
|
|
193
|
+
# This shouldn't happen (already resolved in __post_init__)
|
|
194
|
+
# but handle just in case
|
|
195
|
+
raise ValidationError(f"Invalid enabled_rules state: {self.enabled_rules}. Expected list or 'default'.")
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class RuleSet:
|
|
199
|
+
"""
|
|
200
|
+
Fluent API for building rule configurations.
|
|
201
|
+
|
|
202
|
+
Provides a chainable interface for configuring detection rules.
|
|
203
|
+
|
|
204
|
+
Examples
|
|
205
|
+
--------
|
|
206
|
+
Build a custom rule set:
|
|
207
|
+
|
|
208
|
+
>>> rules = (
|
|
209
|
+
... RuleSet()
|
|
210
|
+
... .beyond_limits()
|
|
211
|
+
... .zone_a(consecutive=2, window=3)
|
|
212
|
+
... .trend(length=6, direction='both')
|
|
213
|
+
... )
|
|
214
|
+
|
|
215
|
+
Convert to list:
|
|
216
|
+
|
|
217
|
+
>>> rule_list = rules.get_rules()
|
|
218
|
+
>>> print(rule_list)
|
|
219
|
+
['rule_1', 'rule_2', 'rule_5']
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
def __init__(self):
|
|
223
|
+
self._rules: list[str] = []
|
|
224
|
+
self._parameters: dict[str, dict] = {}
|
|
225
|
+
|
|
226
|
+
def beyond_limits(self) -> RuleSet:
|
|
227
|
+
"""
|
|
228
|
+
Add Rule 1: Points beyond control limits.
|
|
229
|
+
|
|
230
|
+
Detects any point beyond ±3σ from centerline.
|
|
231
|
+
|
|
232
|
+
Returns
|
|
233
|
+
-------
|
|
234
|
+
RuleSet
|
|
235
|
+
Self for chaining
|
|
236
|
+
"""
|
|
237
|
+
self._rules.append('rule_1')
|
|
238
|
+
return self
|
|
239
|
+
|
|
240
|
+
def zone_a(self, consecutive: int = 2, window: int = 3) -> RuleSet:
|
|
241
|
+
"""
|
|
242
|
+
Add Rule 2: Consecutive points in Zone A.
|
|
243
|
+
|
|
244
|
+
Detects when 2 out of 3 consecutive points fall in Zone A
|
|
245
|
+
(between 2σ and 3σ from centerline).
|
|
246
|
+
|
|
247
|
+
Parameters
|
|
248
|
+
----------
|
|
249
|
+
consecutive : int, default 2
|
|
250
|
+
Number of points that must be in zone
|
|
251
|
+
window : int, default 3
|
|
252
|
+
Window size to check
|
|
253
|
+
|
|
254
|
+
Returns
|
|
255
|
+
-------
|
|
256
|
+
RuleSet
|
|
257
|
+
Self for chaining
|
|
258
|
+
"""
|
|
259
|
+
self._rules.append('rule_2')
|
|
260
|
+
self._parameters['rule_2'] = {'consecutive': consecutive, 'window': window}
|
|
261
|
+
return self
|
|
262
|
+
|
|
263
|
+
def zone_b(self, consecutive: int = 4, window: int = 5) -> RuleSet:
|
|
264
|
+
"""
|
|
265
|
+
Add Rule 3: Consecutive points in Zone B or beyond.
|
|
266
|
+
|
|
267
|
+
Detects when 4 out of 5 consecutive points fall in Zone B or beyond
|
|
268
|
+
(beyond 1σ from centerline).
|
|
269
|
+
|
|
270
|
+
Parameters
|
|
271
|
+
----------
|
|
272
|
+
consecutive : int, default 4
|
|
273
|
+
Number of points that must be in zone
|
|
274
|
+
window : int, default 5
|
|
275
|
+
Window size to check
|
|
276
|
+
|
|
277
|
+
Returns
|
|
278
|
+
-------
|
|
279
|
+
RuleSet
|
|
280
|
+
Self for chaining
|
|
281
|
+
"""
|
|
282
|
+
self._rules.append('rule_3')
|
|
283
|
+
self._parameters['rule_3'] = {'consecutive': consecutive, 'window': window}
|
|
284
|
+
return self
|
|
285
|
+
|
|
286
|
+
def run(self, length: int = 8) -> RuleSet:
|
|
287
|
+
"""
|
|
288
|
+
Add Rule 4: Run above or below centerline.
|
|
289
|
+
|
|
290
|
+
Detects when 8 or more consecutive points fall on the same side
|
|
291
|
+
of the centerline.
|
|
292
|
+
|
|
293
|
+
Parameters
|
|
294
|
+
----------
|
|
295
|
+
length : int, default 8
|
|
296
|
+
Number of consecutive points required
|
|
297
|
+
|
|
298
|
+
Returns
|
|
299
|
+
-------
|
|
300
|
+
RuleSet
|
|
301
|
+
Self for chaining
|
|
302
|
+
"""
|
|
303
|
+
self._rules.append('rule_4')
|
|
304
|
+
self._parameters['rule_4'] = {'length': length}
|
|
305
|
+
return self
|
|
306
|
+
|
|
307
|
+
def trend(self, length: int = 6, direction: Literal['up', 'down', 'both'] = 'both') -> RuleSet:
|
|
308
|
+
"""
|
|
309
|
+
Add Rule 5: Trending points.
|
|
310
|
+
|
|
311
|
+
Detects when 6 or more consecutive points are steadily
|
|
312
|
+
increasing or decreasing.
|
|
313
|
+
|
|
314
|
+
Parameters
|
|
315
|
+
----------
|
|
316
|
+
length : int, default 6
|
|
317
|
+
Number of consecutive points required
|
|
318
|
+
direction : {'up', 'down', 'both'}, default 'both'
|
|
319
|
+
Which direction trends to detect
|
|
320
|
+
|
|
321
|
+
Returns
|
|
322
|
+
-------
|
|
323
|
+
RuleSet
|
|
324
|
+
Self for chaining
|
|
325
|
+
"""
|
|
326
|
+
self._rules.append('rule_5')
|
|
327
|
+
self._parameters['rule_5'] = {'length': length, 'direction': direction}
|
|
328
|
+
return self
|
|
329
|
+
|
|
330
|
+
def oscillation(self, length: int = 14) -> RuleSet:
|
|
331
|
+
"""
|
|
332
|
+
Add Rule 6: Alternating pattern.
|
|
333
|
+
|
|
334
|
+
Detects when 14 or more consecutive points alternate up and down.
|
|
335
|
+
|
|
336
|
+
Parameters
|
|
337
|
+
----------
|
|
338
|
+
length : int, default 14
|
|
339
|
+
Number of consecutive points required
|
|
340
|
+
|
|
341
|
+
Returns
|
|
342
|
+
-------
|
|
343
|
+
RuleSet
|
|
344
|
+
Self for chaining
|
|
345
|
+
"""
|
|
346
|
+
self._rules.append('rule_6')
|
|
347
|
+
self._parameters['rule_6'] = {'length': length}
|
|
348
|
+
return self
|
|
349
|
+
|
|
350
|
+
def reduced_variation(self, length: int = 15) -> RuleSet:
|
|
351
|
+
"""
|
|
352
|
+
Add Rule 7: Points in Zone C (low variation).
|
|
353
|
+
|
|
354
|
+
Detects when 15 or more consecutive points fall within Zone C
|
|
355
|
+
(within ±1σ of centerline), indicating reduced variation.
|
|
356
|
+
|
|
357
|
+
Parameters
|
|
358
|
+
----------
|
|
359
|
+
length : int, default 15
|
|
360
|
+
Number of consecutive points required
|
|
361
|
+
|
|
362
|
+
Returns
|
|
363
|
+
-------
|
|
364
|
+
RuleSet
|
|
365
|
+
Self for chaining
|
|
366
|
+
"""
|
|
367
|
+
self._rules.append('rule_7')
|
|
368
|
+
self._parameters['rule_7'] = {'length': length}
|
|
369
|
+
return self
|
|
370
|
+
|
|
371
|
+
def avoiding_center(self, length: int = 8) -> RuleSet:
|
|
372
|
+
"""
|
|
373
|
+
Add Rule 8: Points avoiding Zone C.
|
|
374
|
+
|
|
375
|
+
Detects when 8 or more consecutive points fall outside Zone C
|
|
376
|
+
(beyond ±1σ from centerline).
|
|
377
|
+
|
|
378
|
+
Parameters
|
|
379
|
+
----------
|
|
380
|
+
length : int, default 8
|
|
381
|
+
Number of consecutive points required
|
|
382
|
+
|
|
383
|
+
Returns
|
|
384
|
+
-------
|
|
385
|
+
RuleSet
|
|
386
|
+
Self for chaining
|
|
387
|
+
"""
|
|
388
|
+
self._rules.append('rule_8')
|
|
389
|
+
self._parameters['rule_8'] = {'length': length}
|
|
390
|
+
return self
|
|
391
|
+
|
|
392
|
+
def custom(self, name: str, detector: Callable, min_observations: int = 1) -> RuleSet:
|
|
393
|
+
"""
|
|
394
|
+
Add a custom detection rule.
|
|
395
|
+
|
|
396
|
+
Parameters
|
|
397
|
+
----------
|
|
398
|
+
name : str
|
|
399
|
+
Rule name
|
|
400
|
+
detector : callable
|
|
401
|
+
Detection function with signature:
|
|
402
|
+
detector(data: DataFrame, stats: dict, value_col: str) -> Series[bool]
|
|
403
|
+
min_observations : int, default 1
|
|
404
|
+
Minimum observations required
|
|
405
|
+
|
|
406
|
+
Returns
|
|
407
|
+
-------
|
|
408
|
+
RuleSet
|
|
409
|
+
Self for chaining
|
|
410
|
+
"""
|
|
411
|
+
self._rules.append(name)
|
|
412
|
+
self._parameters[name] = {'detector': detector, 'min_observations': min_observations}
|
|
413
|
+
return self
|
|
414
|
+
|
|
415
|
+
def to_config(self) -> SignalConfig:
|
|
416
|
+
"""
|
|
417
|
+
Convert to SignalConfig.
|
|
418
|
+
|
|
419
|
+
Returns
|
|
420
|
+
-------
|
|
421
|
+
SignalConfig
|
|
422
|
+
Configuration object
|
|
423
|
+
"""
|
|
424
|
+
return SignalConfig(enabled_rules=self._rules)
|
|
425
|
+
|
|
426
|
+
def get_rules(self) -> list[str]:
|
|
427
|
+
"""
|
|
428
|
+
Get list of enabled rules.
|
|
429
|
+
|
|
430
|
+
Returns
|
|
431
|
+
-------
|
|
432
|
+
list of str
|
|
433
|
+
Rule names
|
|
434
|
+
"""
|
|
435
|
+
return self._rules.copy()
|