aponyx 0.1.18__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 (104) hide show
  1. aponyx/__init__.py +14 -0
  2. aponyx/backtest/__init__.py +31 -0
  3. aponyx/backtest/adapters.py +77 -0
  4. aponyx/backtest/config.py +84 -0
  5. aponyx/backtest/engine.py +560 -0
  6. aponyx/backtest/protocols.py +101 -0
  7. aponyx/backtest/registry.py +334 -0
  8. aponyx/backtest/strategy_catalog.json +50 -0
  9. aponyx/cli/__init__.py +5 -0
  10. aponyx/cli/commands/__init__.py +8 -0
  11. aponyx/cli/commands/clean.py +349 -0
  12. aponyx/cli/commands/list.py +302 -0
  13. aponyx/cli/commands/report.py +167 -0
  14. aponyx/cli/commands/run.py +377 -0
  15. aponyx/cli/main.py +125 -0
  16. aponyx/config/__init__.py +82 -0
  17. aponyx/data/__init__.py +99 -0
  18. aponyx/data/bloomberg_config.py +306 -0
  19. aponyx/data/bloomberg_instruments.json +26 -0
  20. aponyx/data/bloomberg_securities.json +42 -0
  21. aponyx/data/cache.py +294 -0
  22. aponyx/data/fetch.py +659 -0
  23. aponyx/data/fetch_registry.py +135 -0
  24. aponyx/data/loaders.py +205 -0
  25. aponyx/data/providers/__init__.py +13 -0
  26. aponyx/data/providers/bloomberg.py +383 -0
  27. aponyx/data/providers/file.py +111 -0
  28. aponyx/data/registry.py +500 -0
  29. aponyx/data/requirements.py +96 -0
  30. aponyx/data/sample_data.py +415 -0
  31. aponyx/data/schemas.py +60 -0
  32. aponyx/data/sources.py +171 -0
  33. aponyx/data/synthetic_params.json +46 -0
  34. aponyx/data/transforms.py +336 -0
  35. aponyx/data/validation.py +308 -0
  36. aponyx/docs/__init__.py +24 -0
  37. aponyx/docs/adding_data_providers.md +682 -0
  38. aponyx/docs/cdx_knowledge_base.md +455 -0
  39. aponyx/docs/cdx_overlay_strategy.md +135 -0
  40. aponyx/docs/cli_guide.md +607 -0
  41. aponyx/docs/governance_design.md +551 -0
  42. aponyx/docs/logging_design.md +251 -0
  43. aponyx/docs/performance_evaluation_design.md +265 -0
  44. aponyx/docs/python_guidelines.md +786 -0
  45. aponyx/docs/signal_registry_usage.md +369 -0
  46. aponyx/docs/signal_suitability_design.md +558 -0
  47. aponyx/docs/visualization_design.md +277 -0
  48. aponyx/evaluation/__init__.py +11 -0
  49. aponyx/evaluation/performance/__init__.py +24 -0
  50. aponyx/evaluation/performance/adapters.py +109 -0
  51. aponyx/evaluation/performance/analyzer.py +384 -0
  52. aponyx/evaluation/performance/config.py +320 -0
  53. aponyx/evaluation/performance/decomposition.py +304 -0
  54. aponyx/evaluation/performance/metrics.py +761 -0
  55. aponyx/evaluation/performance/registry.py +327 -0
  56. aponyx/evaluation/performance/report.py +541 -0
  57. aponyx/evaluation/suitability/__init__.py +67 -0
  58. aponyx/evaluation/suitability/config.py +143 -0
  59. aponyx/evaluation/suitability/evaluator.py +389 -0
  60. aponyx/evaluation/suitability/registry.py +328 -0
  61. aponyx/evaluation/suitability/report.py +398 -0
  62. aponyx/evaluation/suitability/scoring.py +367 -0
  63. aponyx/evaluation/suitability/tests.py +303 -0
  64. aponyx/examples/01_generate_synthetic_data.py +53 -0
  65. aponyx/examples/02_fetch_data_file.py +82 -0
  66. aponyx/examples/03_fetch_data_bloomberg.py +104 -0
  67. aponyx/examples/04_compute_signal.py +164 -0
  68. aponyx/examples/05_evaluate_suitability.py +224 -0
  69. aponyx/examples/06_run_backtest.py +242 -0
  70. aponyx/examples/07_analyze_performance.py +214 -0
  71. aponyx/examples/08_visualize_results.py +272 -0
  72. aponyx/main.py +7 -0
  73. aponyx/models/__init__.py +45 -0
  74. aponyx/models/config.py +83 -0
  75. aponyx/models/indicator_transformation.json +52 -0
  76. aponyx/models/indicators.py +292 -0
  77. aponyx/models/metadata.py +447 -0
  78. aponyx/models/orchestrator.py +213 -0
  79. aponyx/models/registry.py +860 -0
  80. aponyx/models/score_transformation.json +42 -0
  81. aponyx/models/signal_catalog.json +29 -0
  82. aponyx/models/signal_composer.py +513 -0
  83. aponyx/models/signal_transformation.json +29 -0
  84. aponyx/persistence/__init__.py +16 -0
  85. aponyx/persistence/json_io.py +132 -0
  86. aponyx/persistence/parquet_io.py +378 -0
  87. aponyx/py.typed +0 -0
  88. aponyx/reporting/__init__.py +10 -0
  89. aponyx/reporting/generator.py +517 -0
  90. aponyx/visualization/__init__.py +20 -0
  91. aponyx/visualization/app.py +37 -0
  92. aponyx/visualization/plots.py +309 -0
  93. aponyx/visualization/visualizer.py +242 -0
  94. aponyx/workflows/__init__.py +18 -0
  95. aponyx/workflows/concrete_steps.py +720 -0
  96. aponyx/workflows/config.py +122 -0
  97. aponyx/workflows/engine.py +279 -0
  98. aponyx/workflows/registry.py +116 -0
  99. aponyx/workflows/steps.py +180 -0
  100. aponyx-0.1.18.dist-info/METADATA +552 -0
  101. aponyx-0.1.18.dist-info/RECORD +104 -0
  102. aponyx-0.1.18.dist-info/WHEEL +4 -0
  103. aponyx-0.1.18.dist-info/entry_points.txt +2 -0
  104. aponyx-0.1.18.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,369 @@
1
+ ````markdown
2
+ # Signal Registry Pattern - Usage Guide
3
+
4
+ ## Overview
5
+
6
+ The signal registry infrastructure enables scalable signal research through a **four-stage transformation pipeline**:
7
+ 1. **Indicator Transformations** — Raw economic metrics from market data
8
+ 2. **Score Transformations** — Normalization (z-score, volatility adjustment)
9
+ 3. **Signal Transformations** — Trading rules (bounds, neutral zones)
10
+ 4. **Signals** — Composed from all three transformation stages
11
+
12
+ Add new signals by editing JSON catalogs instead of modifying code. Each signal is evaluated independently to establish clear performance attribution.
13
+
14
+ ## Quick Start
15
+
16
+ ### Basic Usage
17
+
18
+ ```python
19
+ from aponyx.models import (
20
+ IndicatorTransformationRegistry,
21
+ ScoreTransformationRegistry,
22
+ SignalTransformationRegistry,
23
+ SignalRegistry,
24
+ )
25
+ from aponyx.models.signal_composer import compose_signal
26
+ from aponyx.config import (
27
+ INDICATOR_TRANSFORMATION_PATH,
28
+ SCORE_TRANSFORMATION_PATH,
29
+ SIGNAL_TRANSFORMATION_PATH,
30
+ SIGNAL_CATALOG_PATH,
31
+ )
32
+ from aponyx.backtest import run_backtest, BacktestConfig
33
+
34
+ # Load all four registries
35
+ indicator_reg = IndicatorTransformationRegistry(INDICATOR_TRANSFORMATION_PATH)
36
+ score_reg = ScoreTransformationRegistry(SCORE_TRANSFORMATION_PATH)
37
+ signal_trans_reg = SignalTransformationRegistry(SIGNAL_TRANSFORMATION_PATH)
38
+ signal_reg = SignalRegistry(SIGNAL_CATALOG_PATH)
39
+
40
+ # Prepare market data
41
+ market_data = {
42
+ "cdx": cdx_df, # Must have 'spread' column
43
+ "vix": vix_df, # Must have 'level' column
44
+ "etf": etf_df, # Must have 'spread' column
45
+ }
46
+
47
+ # Compose a signal (four-stage pipeline)
48
+ signal = compose_signal(
49
+ signal_name="cdx_etf_basis",
50
+ market_data=market_data,
51
+ indicator_registry=indicator_reg,
52
+ score_registry=score_reg,
53
+ signal_transformation_registry=signal_trans_reg,
54
+ signal_registry=signal_reg,
55
+ )
56
+
57
+ # Run backtest
58
+ config = BacktestConfig(position_size_mm=10.0)
59
+ result = run_backtest(signal, cdx_df["spread"], config)
60
+ print(f"Sharpe Ratio: {result.metrics['sharpe_ratio']:.2f}")
61
+ ```
62
+
63
+ ## Adding a New Signal
64
+
65
+ ### Step 1: Create Indicator Function (if needed)
66
+
67
+ Add to `src/aponyx/models/indicators.py`:
68
+
69
+ ```python
70
+ def compute_my_new_indicator(
71
+ cdx_df: pd.DataFrame,
72
+ other_df: pd.DataFrame,
73
+ ) -> pd.Series:
74
+ """
75
+ Compute my new indicator in basis points.
76
+
77
+ Outputs economically interpretable values WITHOUT normalization.
78
+ Score transformations are applied at signal composition layer.
79
+ """
80
+ # Return raw values in natural units (bps, ratio, etc.)
81
+ return cdx_df["spread"] - other_df["spread"]
82
+ ```
83
+
84
+ ### Step 2: Register Indicator Transformation
85
+
86
+ Edit `src/aponyx/models/indicator_transformation.json`:
87
+
88
+ ```json
89
+ {
90
+ "name": "my_new_indicator",
91
+ "description": "CDX-other spread differential in basis points",
92
+ "compute_function_name": "compute_my_new_indicator",
93
+ "data_requirements": {
94
+ "cdx": "spread",
95
+ "other": "spread"
96
+ },
97
+ "default_securities": {
98
+ "cdx": "cdx_ig_5y",
99
+ "other": "other_security"
100
+ },
101
+ "output_units": "basis_points",
102
+ "parameters": {},
103
+ "enabled": true
104
+ }
105
+ ```
106
+
107
+ ### Step 3: Define Signal (referencing all three transformations)
108
+
109
+ Edit `src/aponyx/models/signal_catalog.json`:
110
+
111
+ ```json
112
+ {
113
+ "name": "my_new_signal",
114
+ "description": "Trading signal based on my indicator",
115
+ "indicator_transformation": "my_new_indicator",
116
+ "score_transformation": "z_score_20d",
117
+ "signal_transformation": "passthrough",
118
+ "enabled": true,
119
+ "sign_multiplier": 1
120
+ }
121
+ ```
122
+
123
+ ### Step 4: Use the Signal
124
+
125
+ ```python
126
+ # Registry automatically picks up the new signal
127
+ signal = compose_signal(
128
+ signal_name="my_new_signal",
129
+ market_data=market_data,
130
+ indicator_registry=indicator_reg,
131
+ score_registry=score_reg,
132
+ signal_transformation_registry=signal_trans_reg,
133
+ signal_registry=signal_reg,
134
+ )
135
+
136
+ # Run backtest to evaluate performance
137
+ config = BacktestConfig(position_size_mm=10.0)
138
+ result = run_backtest(signal, cdx_df["spread"], config)
139
+ print(f"Sharpe Ratio: {result.metrics['sharpe_ratio']:.2f}")
140
+ ```
141
+
142
+ ## Four-Stage Transformation Pipeline
143
+
144
+ Every signal is composed through four stages:
145
+
146
+ | Stage | Catalog | Purpose | Example |
147
+ |-------|---------|---------|---------|
148
+ | **Indicator** | `indicator_transformation.json` | Compute raw economic metric | Spread difference (bps) |
149
+ | **Score** | `score_transformation.json` | Normalize to common scale | Z-score over 20 days |
150
+ | **Signal** | `signal_transformation.json` | Apply trading rules | Bounds [-1.5, 1.5], neutral zone |
151
+ | **Composition** | `signal_catalog.json` | Reference all three | Links stages together |
152
+
153
+ ### Transformation Catalog Files
154
+
155
+ **indicator_transformation.json** — Raw metrics:
156
+ ```json
157
+ {
158
+ "name": "cdx_etf_spread_diff",
159
+ "compute_function_name": "compute_cdx_etf_spread_diff",
160
+ "data_requirements": {"cdx": "spread", "etf": "spread"},
161
+ "output_units": "basis_points"
162
+ }
163
+ ```
164
+
165
+ **score_transformation.json** — Normalization:
166
+ ```json
167
+ {
168
+ "name": "z_score_20d",
169
+ "transform_type": "z_score",
170
+ "parameters": {"window": 20, "min_periods": 10}
171
+ }
172
+ ```
173
+
174
+ **signal_transformation.json** — Trading rules:
175
+ ```json
176
+ {
177
+ "name": "bounded_1_5",
178
+ "scaling": 1.0,
179
+ "floor": -1.5,
180
+ "cap": 1.5,
181
+ "neutral_range": [-0.25, 0.25]
182
+ }
183
+ ```
184
+
185
+ **signal_catalog.json** — Composition:
186
+ ```json
187
+ {
188
+ "name": "cdx_etf_basis",
189
+ "indicator_transformation": "cdx_etf_spread_diff",
190
+ "score_transformation": "z_score_20d",
191
+ "signal_transformation": "passthrough",
192
+ "sign_multiplier": 1
193
+ }
194
+ ```
195
+
196
+ ## Signal Catalog Schema
197
+
198
+ ### SignalMetadata Fields
199
+
200
+ | Field | Type | Description |
201
+ |-------|------|-------------|
202
+ | `name` | string | Unique signal identifier (snake_case) |
203
+ | `description` | string | Human-readable signal description |
204
+ | `indicator_transformation` | string | Reference to indicator_transformation.json |
205
+ | `score_transformation` | string | Reference to score_transformation.json |
206
+ | `signal_transformation` | string | Reference to signal_transformation.json |
207
+ | `enabled` | boolean | Whether to compute this signal |
208
+ | `sign_multiplier` | int | Sign adjustment (1 or -1) |
209
+
210
+ ## Integration with Backtesting
211
+
212
+ The backtest layer accepts any signal series. Current `BacktestConfig` parameters:
213
+
214
+ ```python
215
+ from aponyx.backtest import BacktestConfig, run_backtest
216
+
217
+ # Backtest configuration
218
+ config = BacktestConfig(
219
+ position_size_mm=10.0, # Notional in millions
220
+ sizing_mode="proportional", # 'binary' or 'proportional' (default)
221
+ stop_loss_pct=5.0, # Optional: stop at 5% loss
222
+ take_profit_pct=10.0, # Optional: take profit at 10%
223
+ max_holding_days=None, # Optional: max holding period
224
+ transaction_cost_bps=1.0, # Round-trip cost in bps
225
+ dv01_per_million=475.0, # DV01 for risk calculations ($475 per $1MM)
226
+ signal_lag=1, # Days to lag signal (prevent look-ahead)
227
+ )
228
+
229
+ result = run_backtest(signal, cdx_df["spread"], config)
230
+ ```
231
+
232
+ ### Position Sizing Modes
233
+
234
+ **Proportional (default):** Position scales with signal magnitude
235
+ ```python
236
+ config = BacktestConfig(
237
+ position_size_mm=10.0,
238
+ sizing_mode="proportional", # Default: position = signal × 10MM
239
+ )
240
+ ```
241
+
242
+ **Binary:** Full position for any non-zero signal
243
+ ```python
244
+ config = BacktestConfig(
245
+ position_size_mm=10.0,
246
+ sizing_mode="binary", # Position = ±10MM regardless of signal magnitude
247
+ )
248
+ ```
249
+
250
+ ## Runtime Overrides
251
+
252
+ Override transformation stages at compose time:
253
+
254
+ ```python
255
+ # Use 60-day z-score instead of default 20-day
256
+ signal = compose_signal(
257
+ signal_name="cdx_etf_basis",
258
+ market_data=market_data,
259
+ indicator_registry=indicator_reg,
260
+ score_registry=score_reg,
261
+ signal_transformation_registry=signal_trans_reg,
262
+ signal_registry=signal_reg,
263
+ score_transformation_override="z_score_60d",
264
+ )
265
+
266
+ # With intermediate stage inspection
267
+ result = compose_signal(
268
+ signal_name="cdx_etf_basis",
269
+ market_data=market_data,
270
+ indicator_registry=indicator_reg,
271
+ score_registry=score_reg,
272
+ signal_transformation_registry=signal_trans_reg,
273
+ signal_registry=signal_reg,
274
+ include_intermediates=True,
275
+ )
276
+ print(result["indicator"].tail()) # Raw indicator
277
+ print(result["score"].tail()) # Normalized score
278
+ print(result["signal"].tail()) # Final signal
279
+ ```
280
+
281
+ ## Using Strategy Registry
282
+
283
+ Load strategy configurations from catalog:
284
+
285
+ ```python
286
+ from aponyx.backtest import StrategyRegistry
287
+ from aponyx.config import STRATEGY_CATALOG_PATH
288
+
289
+ # Load strategies from catalog
290
+ strategy_reg = StrategyRegistry(STRATEGY_CATALOG_PATH)
291
+
292
+ # Get strategy metadata and convert to config
293
+ metadata = strategy_reg.get_metadata("balanced")
294
+ config = metadata.to_config()
295
+
296
+ # Override specific parameters
297
+ config = metadata.to_config(
298
+ position_size_mm_override=20.0, # Use 20MM instead of catalog default
299
+ )
300
+
301
+ result = run_backtest(signal, cdx_df["spread"], config)
302
+ ```
303
+
304
+ ## Best Practices
305
+
306
+ 1. **Follow four-stage pipeline** — All signals use compose_signal() (no exceptions)
307
+ 2. **Follow signal convention** — Positive = long credit risk (buy CDX)
308
+ 3. **Log operations** using module-level logger with %-formatting
309
+ 4. **Validate data requirements** are met before computing
310
+ 5. **Include signal description** in catalog for documentation
311
+ 6. **Test determinism** to ensure reproducible results
312
+ 7. **Use runtime overrides** for experimentation without modifying catalogs
313
+
314
+ ## Debugging Signals
315
+
316
+ ### Enable Debug Logging
317
+
318
+ ```python
319
+ import logging
320
+ logging.basicConfig(level=logging.DEBUG)
321
+
322
+ # Signal computations will log details:
323
+ # DEBUG - Computing indicator: cdx_etf_spread_diff
324
+ # DEBUG - Applying score transformation: z_score_20d
325
+ # DEBUG - Applying signal transformation: passthrough
326
+ ```
327
+
328
+ ### Inspect Intermediate Values
329
+
330
+ ```python
331
+ result = compose_signal(
332
+ signal_name="cdx_etf_basis",
333
+ market_data=market_data,
334
+ indicator_registry=indicator_reg,
335
+ score_registry=score_reg,
336
+ signal_transformation_registry=signal_trans_reg,
337
+ signal_registry=signal_reg,
338
+ include_intermediates=True,
339
+ )
340
+
341
+ print("Indicator (raw bps):", result["indicator"].describe())
342
+ print("Score (normalized):", result["score"].describe())
343
+ print("Signal (final):", result["signal"].describe())
344
+ ```
345
+
346
+ ### Common Issues
347
+
348
+ **Signal returns all NaN values:**
349
+ ```python
350
+ # Check data alignment
351
+ print(f"CDX: {cdx_df.index.min()} to {cdx_df.index.max()}")
352
+ print(f"ETF: {etf_df.index.min()} to {etf_df.index.max()}")
353
+
354
+ # Ensure indices overlap
355
+ ```
356
+
357
+ **Signal not found in registry:**
358
+ ```python
359
+ # List all enabled signals
360
+ enabled = signal_reg.get_enabled()
361
+ print(f"Enabled signals: {list(enabled.keys())}")
362
+ ```
363
+
364
+ ---
365
+
366
+ **Maintained by:** stabilefrisur
367
+ **Last Updated:** December 13, 2025
368
+
369
+ ````