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.
- aponyx/__init__.py +14 -0
- aponyx/backtest/__init__.py +31 -0
- aponyx/backtest/adapters.py +77 -0
- aponyx/backtest/config.py +84 -0
- aponyx/backtest/engine.py +560 -0
- aponyx/backtest/protocols.py +101 -0
- aponyx/backtest/registry.py +334 -0
- aponyx/backtest/strategy_catalog.json +50 -0
- aponyx/cli/__init__.py +5 -0
- aponyx/cli/commands/__init__.py +8 -0
- aponyx/cli/commands/clean.py +349 -0
- aponyx/cli/commands/list.py +302 -0
- aponyx/cli/commands/report.py +167 -0
- aponyx/cli/commands/run.py +377 -0
- aponyx/cli/main.py +125 -0
- aponyx/config/__init__.py +82 -0
- aponyx/data/__init__.py +99 -0
- aponyx/data/bloomberg_config.py +306 -0
- aponyx/data/bloomberg_instruments.json +26 -0
- aponyx/data/bloomberg_securities.json +42 -0
- aponyx/data/cache.py +294 -0
- aponyx/data/fetch.py +659 -0
- aponyx/data/fetch_registry.py +135 -0
- aponyx/data/loaders.py +205 -0
- aponyx/data/providers/__init__.py +13 -0
- aponyx/data/providers/bloomberg.py +383 -0
- aponyx/data/providers/file.py +111 -0
- aponyx/data/registry.py +500 -0
- aponyx/data/requirements.py +96 -0
- aponyx/data/sample_data.py +415 -0
- aponyx/data/schemas.py +60 -0
- aponyx/data/sources.py +171 -0
- aponyx/data/synthetic_params.json +46 -0
- aponyx/data/transforms.py +336 -0
- aponyx/data/validation.py +308 -0
- aponyx/docs/__init__.py +24 -0
- aponyx/docs/adding_data_providers.md +682 -0
- aponyx/docs/cdx_knowledge_base.md +455 -0
- aponyx/docs/cdx_overlay_strategy.md +135 -0
- aponyx/docs/cli_guide.md +607 -0
- aponyx/docs/governance_design.md +551 -0
- aponyx/docs/logging_design.md +251 -0
- aponyx/docs/performance_evaluation_design.md +265 -0
- aponyx/docs/python_guidelines.md +786 -0
- aponyx/docs/signal_registry_usage.md +369 -0
- aponyx/docs/signal_suitability_design.md +558 -0
- aponyx/docs/visualization_design.md +277 -0
- aponyx/evaluation/__init__.py +11 -0
- aponyx/evaluation/performance/__init__.py +24 -0
- aponyx/evaluation/performance/adapters.py +109 -0
- aponyx/evaluation/performance/analyzer.py +384 -0
- aponyx/evaluation/performance/config.py +320 -0
- aponyx/evaluation/performance/decomposition.py +304 -0
- aponyx/evaluation/performance/metrics.py +761 -0
- aponyx/evaluation/performance/registry.py +327 -0
- aponyx/evaluation/performance/report.py +541 -0
- aponyx/evaluation/suitability/__init__.py +67 -0
- aponyx/evaluation/suitability/config.py +143 -0
- aponyx/evaluation/suitability/evaluator.py +389 -0
- aponyx/evaluation/suitability/registry.py +328 -0
- aponyx/evaluation/suitability/report.py +398 -0
- aponyx/evaluation/suitability/scoring.py +367 -0
- aponyx/evaluation/suitability/tests.py +303 -0
- aponyx/examples/01_generate_synthetic_data.py +53 -0
- aponyx/examples/02_fetch_data_file.py +82 -0
- aponyx/examples/03_fetch_data_bloomberg.py +104 -0
- aponyx/examples/04_compute_signal.py +164 -0
- aponyx/examples/05_evaluate_suitability.py +224 -0
- aponyx/examples/06_run_backtest.py +242 -0
- aponyx/examples/07_analyze_performance.py +214 -0
- aponyx/examples/08_visualize_results.py +272 -0
- aponyx/main.py +7 -0
- aponyx/models/__init__.py +45 -0
- aponyx/models/config.py +83 -0
- aponyx/models/indicator_transformation.json +52 -0
- aponyx/models/indicators.py +292 -0
- aponyx/models/metadata.py +447 -0
- aponyx/models/orchestrator.py +213 -0
- aponyx/models/registry.py +860 -0
- aponyx/models/score_transformation.json +42 -0
- aponyx/models/signal_catalog.json +29 -0
- aponyx/models/signal_composer.py +513 -0
- aponyx/models/signal_transformation.json +29 -0
- aponyx/persistence/__init__.py +16 -0
- aponyx/persistence/json_io.py +132 -0
- aponyx/persistence/parquet_io.py +378 -0
- aponyx/py.typed +0 -0
- aponyx/reporting/__init__.py +10 -0
- aponyx/reporting/generator.py +517 -0
- aponyx/visualization/__init__.py +20 -0
- aponyx/visualization/app.py +37 -0
- aponyx/visualization/plots.py +309 -0
- aponyx/visualization/visualizer.py +242 -0
- aponyx/workflows/__init__.py +18 -0
- aponyx/workflows/concrete_steps.py +720 -0
- aponyx/workflows/config.py +122 -0
- aponyx/workflows/engine.py +279 -0
- aponyx/workflows/registry.py +116 -0
- aponyx/workflows/steps.py +180 -0
- aponyx-0.1.18.dist-info/METADATA +552 -0
- aponyx-0.1.18.dist-info/RECORD +104 -0
- aponyx-0.1.18.dist-info/WHEEL +4 -0
- aponyx-0.1.18.dist-info/entry_points.txt +2 -0
- aponyx-0.1.18.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Generate visualization charts for backtest results.
|
|
3
|
+
|
|
4
|
+
Prerequisites
|
|
5
|
+
-------------
|
|
6
|
+
Backtest results saved from backtest execution (06_run_backtest.py):
|
|
7
|
+
- P&L file: data/workflows/backtests/{signal}_{strategy}_pnl.parquet
|
|
8
|
+
- Positions file: data/workflows/backtests/{signal}_{strategy}_positions.parquet
|
|
9
|
+
|
|
10
|
+
Outputs
|
|
11
|
+
-------
|
|
12
|
+
Three Plotly figure objects:
|
|
13
|
+
- Equity curve: cumulative P&L over time
|
|
14
|
+
- Drawdown chart: underwater equity visualization
|
|
15
|
+
- Signal plot: time series of signal values with thresholds
|
|
16
|
+
|
|
17
|
+
Examples
|
|
18
|
+
--------
|
|
19
|
+
Run from project root:
|
|
20
|
+
python -m aponyx.examples.08_visualize_results
|
|
21
|
+
|
|
22
|
+
Expected output: Three interactive Plotly charts displayed or saved.
|
|
23
|
+
Figures can be rendered in notebooks, Streamlit apps, or exported to HTML.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import pandas as pd
|
|
27
|
+
import plotly.graph_objects as go
|
|
28
|
+
|
|
29
|
+
from aponyx.config import DATA_WORKFLOWS_DIR
|
|
30
|
+
from aponyx.persistence import load_parquet
|
|
31
|
+
from aponyx.visualization import plot_equity_curve, plot_drawdown, plot_signal
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def main() -> dict[str, go.Figure]:
|
|
35
|
+
"""
|
|
36
|
+
Execute visualization workflow.
|
|
37
|
+
|
|
38
|
+
Loads backtest results and generates three key charts:
|
|
39
|
+
equity curve, drawdown, and signal time series.
|
|
40
|
+
|
|
41
|
+
Returns
|
|
42
|
+
-------
|
|
43
|
+
dict[str, go.Figure]
|
|
44
|
+
Dictionary of figure names to Plotly figure objects.
|
|
45
|
+
|
|
46
|
+
Notes
|
|
47
|
+
-----
|
|
48
|
+
Figures are returned for flexible rendering (Streamlit, Jupyter, HTML).
|
|
49
|
+
To display in Jupyter: fig.show()
|
|
50
|
+
To save to HTML: fig.write_html("output.html")
|
|
51
|
+
To display in Streamlit: st.plotly_chart(fig)
|
|
52
|
+
"""
|
|
53
|
+
signal_name, strategy_name = define_visualization_parameters()
|
|
54
|
+
pnl, positions = load_backtest_data(signal_name, strategy_name)
|
|
55
|
+
return generate_all_charts(pnl, positions, signal_name, strategy_name)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def define_visualization_parameters() -> tuple[str, str]:
|
|
59
|
+
"""
|
|
60
|
+
Define visualization parameters.
|
|
61
|
+
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
64
|
+
tuple[str, str]
|
|
65
|
+
Signal name and strategy name.
|
|
66
|
+
|
|
67
|
+
Notes
|
|
68
|
+
-----
|
|
69
|
+
Must match the signal-strategy combination from backtest step.
|
|
70
|
+
"""
|
|
71
|
+
signal_name = "spread_momentum"
|
|
72
|
+
strategy_name = "balanced"
|
|
73
|
+
return signal_name, strategy_name
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def load_backtest_data(
|
|
77
|
+
signal_name: str,
|
|
78
|
+
strategy_name: str,
|
|
79
|
+
) -> tuple[pd.DataFrame, pd.DataFrame]:
|
|
80
|
+
"""
|
|
81
|
+
Load P&L and positions from backtest results.
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
signal_name : str
|
|
86
|
+
Name of signal.
|
|
87
|
+
strategy_name : str
|
|
88
|
+
Name of strategy.
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
tuple[pd.DataFrame, pd.DataFrame]
|
|
93
|
+
P&L DataFrame and positions DataFrame.
|
|
94
|
+
|
|
95
|
+
Notes
|
|
96
|
+
-----
|
|
97
|
+
Loads data saved by 06_run_backtest.py from processed directory.
|
|
98
|
+
P&L DataFrame contains net_pnl column for equity curve.
|
|
99
|
+
Positions DataFrame contains signal column for signal plot.
|
|
100
|
+
"""
|
|
101
|
+
backtests_dir = DATA_WORKFLOWS_DIR / "backtests"
|
|
102
|
+
|
|
103
|
+
pnl_path = backtests_dir / f"{signal_name}_{strategy_name}_pnl.parquet"
|
|
104
|
+
positions_path = backtests_dir / f"{signal_name}_{strategy_name}_positions.parquet"
|
|
105
|
+
|
|
106
|
+
pnl = load_parquet(pnl_path)
|
|
107
|
+
positions = load_parquet(positions_path)
|
|
108
|
+
|
|
109
|
+
return pnl, positions
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def generate_all_charts(
|
|
113
|
+
pnl: pd.DataFrame,
|
|
114
|
+
positions: pd.DataFrame,
|
|
115
|
+
signal_name: str,
|
|
116
|
+
strategy_name: str,
|
|
117
|
+
) -> dict[str, go.Figure]:
|
|
118
|
+
"""
|
|
119
|
+
Generate all visualization charts.
|
|
120
|
+
|
|
121
|
+
Parameters
|
|
122
|
+
----------
|
|
123
|
+
pnl : pd.DataFrame
|
|
124
|
+
P&L data with net_pnl column.
|
|
125
|
+
positions : pd.DataFrame
|
|
126
|
+
Positions data with signal column.
|
|
127
|
+
signal_name : str
|
|
128
|
+
Signal name for titles.
|
|
129
|
+
strategy_name : str
|
|
130
|
+
Strategy name for titles.
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
dict[str, go.Figure]
|
|
135
|
+
Dictionary with keys: equity_curve, drawdown, signal.
|
|
136
|
+
|
|
137
|
+
Notes
|
|
138
|
+
-----
|
|
139
|
+
Charts are configured for research presentation:
|
|
140
|
+
- Equity curve shows drawdown shading for regime visualization
|
|
141
|
+
- Drawdown uses underwater chart format (absolute dollars)
|
|
142
|
+
- Signal includes ±2 threshold lines for regime boundaries
|
|
143
|
+
"""
|
|
144
|
+
figures = {}
|
|
145
|
+
|
|
146
|
+
figures["equity_curve"] = create_equity_curve(
|
|
147
|
+
pnl,
|
|
148
|
+
signal_name,
|
|
149
|
+
strategy_name,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
figures["drawdown"] = create_drawdown_chart(
|
|
153
|
+
pnl,
|
|
154
|
+
signal_name,
|
|
155
|
+
strategy_name,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
figures["signal"] = create_signal_chart(
|
|
159
|
+
positions,
|
|
160
|
+
signal_name,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
return figures
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def create_equity_curve(
|
|
167
|
+
pnl: pd.DataFrame,
|
|
168
|
+
signal_name: str,
|
|
169
|
+
strategy_name: str,
|
|
170
|
+
) -> go.Figure:
|
|
171
|
+
"""
|
|
172
|
+
Create equity curve chart with drawdown shading.
|
|
173
|
+
|
|
174
|
+
Parameters
|
|
175
|
+
----------
|
|
176
|
+
pnl : pd.DataFrame
|
|
177
|
+
P&L data with net_pnl column.
|
|
178
|
+
signal_name : str
|
|
179
|
+
Signal name for title.
|
|
180
|
+
strategy_name : str
|
|
181
|
+
Strategy name for title.
|
|
182
|
+
|
|
183
|
+
Returns
|
|
184
|
+
-------
|
|
185
|
+
go.Figure
|
|
186
|
+
Plotly equity curve figure.
|
|
187
|
+
|
|
188
|
+
Notes
|
|
189
|
+
-----
|
|
190
|
+
Uses net_pnl column for cumulative P&L calculation.
|
|
191
|
+
Drawdown shading highlights underwater periods in red.
|
|
192
|
+
"""
|
|
193
|
+
title = f"Equity Curve: {signal_name} ({strategy_name})"
|
|
194
|
+
return plot_equity_curve(
|
|
195
|
+
pnl["net_pnl"],
|
|
196
|
+
title=title,
|
|
197
|
+
show_drawdown_shading=True,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def create_drawdown_chart(
|
|
202
|
+
pnl: pd.DataFrame,
|
|
203
|
+
signal_name: str,
|
|
204
|
+
strategy_name: str,
|
|
205
|
+
) -> go.Figure:
|
|
206
|
+
"""
|
|
207
|
+
Create drawdown chart showing peak-to-trough decline.
|
|
208
|
+
|
|
209
|
+
Parameters
|
|
210
|
+
----------
|
|
211
|
+
pnl : pd.DataFrame
|
|
212
|
+
P&L data with net_pnl column.
|
|
213
|
+
signal_name : str
|
|
214
|
+
Signal name for title.
|
|
215
|
+
strategy_name : str
|
|
216
|
+
Strategy name for title.
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
go.Figure
|
|
221
|
+
Plotly drawdown figure.
|
|
222
|
+
|
|
223
|
+
Notes
|
|
224
|
+
-----
|
|
225
|
+
Uses underwater chart format (absolute dollars).
|
|
226
|
+
Drawdown is always non-positive (zero at peaks, negative otherwise).
|
|
227
|
+
"""
|
|
228
|
+
title = f"Drawdown: {signal_name} ({strategy_name})"
|
|
229
|
+
return plot_drawdown(
|
|
230
|
+
pnl["net_pnl"],
|
|
231
|
+
title=title,
|
|
232
|
+
show_underwater_chart=True,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def create_signal_chart(
|
|
237
|
+
positions: pd.DataFrame,
|
|
238
|
+
signal_name: str,
|
|
239
|
+
) -> go.Figure:
|
|
240
|
+
"""
|
|
241
|
+
Create signal time series chart with threshold lines.
|
|
242
|
+
|
|
243
|
+
Parameters
|
|
244
|
+
----------
|
|
245
|
+
positions : pd.DataFrame
|
|
246
|
+
Positions data with signal column.
|
|
247
|
+
signal_name : str
|
|
248
|
+
Signal name for title.
|
|
249
|
+
|
|
250
|
+
Returns
|
|
251
|
+
-------
|
|
252
|
+
go.Figure
|
|
253
|
+
Plotly signal figure.
|
|
254
|
+
|
|
255
|
+
Notes
|
|
256
|
+
-----
|
|
257
|
+
Threshold lines at ±2 mark typical entry/exit levels.
|
|
258
|
+
Signal convention: positive = long credit risk (buy CDX).
|
|
259
|
+
"""
|
|
260
|
+
title = f"Signal: {signal_name}"
|
|
261
|
+
signal = positions["signal"]
|
|
262
|
+
signal.name = signal_name
|
|
263
|
+
|
|
264
|
+
return plot_signal(
|
|
265
|
+
signal,
|
|
266
|
+
title=title,
|
|
267
|
+
threshold_lines=[-2.0, 2.0],
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
if __name__ == "__main__":
|
|
272
|
+
main()
|
aponyx/main.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Models layer for systematic credit strategies.
|
|
3
|
+
|
|
4
|
+
This module provides signal generation via the four-stage transformation pipeline:
|
|
5
|
+
Security → Indicator → Score → Signal → Position
|
|
6
|
+
|
|
7
|
+
Module Organization:
|
|
8
|
+
-------------------
|
|
9
|
+
metadata.py - Metadata dataclasses (SignalMetadata, IndicatorMetadata, TransformationMetadata, SignalTransformationMetadata)
|
|
10
|
+
registry.py - Registry classes for catalog management (IndicatorTransformationRegistry, ScoreTransformationRegistry, SignalTransformationRegistry, SignalRegistry)
|
|
11
|
+
orchestrator.py - compute_registered_signals() batch computation
|
|
12
|
+
signal_composer.py - compose_signal() four-stage pipeline composition
|
|
13
|
+
indicators.py - Indicator compute functions
|
|
14
|
+
config.py - SignalConfig dataclass
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from .config import SignalConfig
|
|
18
|
+
from .metadata import (
|
|
19
|
+
CatalogValidationError,
|
|
20
|
+
IndicatorMetadata,
|
|
21
|
+
SignalMetadata,
|
|
22
|
+
SignalTransformationMetadata,
|
|
23
|
+
TransformationMetadata,
|
|
24
|
+
)
|
|
25
|
+
from .orchestrator import compute_registered_signals
|
|
26
|
+
from .registry import (
|
|
27
|
+
IndicatorTransformationRegistry,
|
|
28
|
+
ScoreTransformationRegistry,
|
|
29
|
+
SignalRegistry,
|
|
30
|
+
SignalTransformationRegistry,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"CatalogValidationError",
|
|
35
|
+
"IndicatorMetadata",
|
|
36
|
+
"IndicatorTransformationRegistry",
|
|
37
|
+
"ScoreTransformationRegistry",
|
|
38
|
+
"SignalConfig",
|
|
39
|
+
"SignalMetadata",
|
|
40
|
+
"SignalRegistry",
|
|
41
|
+
"SignalTransformationMetadata",
|
|
42
|
+
"SignalTransformationRegistry",
|
|
43
|
+
"TransformationMetadata",
|
|
44
|
+
"compute_registered_signals",
|
|
45
|
+
]
|
aponyx/models/config.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration dataclasses for indicator and signal generation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class IndicatorConfig:
|
|
10
|
+
"""
|
|
11
|
+
Runtime configuration parameters for indicator computation.
|
|
12
|
+
|
|
13
|
+
Note: Most indicator parameters are defined at catalog-time in IndicatorMetadata.
|
|
14
|
+
This config is for runtime overrides and caching behavior.
|
|
15
|
+
|
|
16
|
+
Attributes
|
|
17
|
+
----------
|
|
18
|
+
use_cache : bool
|
|
19
|
+
Whether to use cached indicator values if available.
|
|
20
|
+
force_recompute : bool
|
|
21
|
+
Force recomputation even if cache exists.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
use_cache: bool = True
|
|
25
|
+
force_recompute: bool = False
|
|
26
|
+
|
|
27
|
+
def __post_init__(self) -> None:
|
|
28
|
+
"""Validate configuration parameters."""
|
|
29
|
+
if self.use_cache and self.force_recompute:
|
|
30
|
+
raise ValueError("Cannot set both use_cache and force_recompute to True")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class TransformationConfig:
|
|
35
|
+
"""
|
|
36
|
+
Runtime configuration parameters for signal transformations.
|
|
37
|
+
|
|
38
|
+
Note: Most transformation parameters are defined at catalog-time in
|
|
39
|
+
TransformationMetadata. This config is for runtime behavior.
|
|
40
|
+
|
|
41
|
+
Attributes
|
|
42
|
+
----------
|
|
43
|
+
min_valid_pct : float
|
|
44
|
+
Minimum percentage of valid (non-NaN) values required after transformation.
|
|
45
|
+
Values between 0.0 and 1.0. Default: 0.5 (50%)
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
min_valid_pct: float = 0.5
|
|
49
|
+
|
|
50
|
+
def __post_init__(self) -> None:
|
|
51
|
+
"""Validate configuration parameters."""
|
|
52
|
+
if not 0.0 <= self.min_valid_pct <= 1.0:
|
|
53
|
+
raise ValueError(
|
|
54
|
+
f"min_valid_pct must be between 0.0 and 1.0, got {self.min_valid_pct}"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(frozen=True)
|
|
59
|
+
class SignalConfig:
|
|
60
|
+
"""
|
|
61
|
+
Configuration parameters for individual signal computation.
|
|
62
|
+
|
|
63
|
+
Attributes
|
|
64
|
+
----------
|
|
65
|
+
lookback : int
|
|
66
|
+
Rolling window size for normalization and statistics.
|
|
67
|
+
min_periods : int
|
|
68
|
+
Minimum observations required for valid calculation.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
lookback: int = 20
|
|
72
|
+
min_periods: int = 10
|
|
73
|
+
|
|
74
|
+
def __post_init__(self) -> None:
|
|
75
|
+
"""Validate configuration parameters."""
|
|
76
|
+
if self.lookback <= 0:
|
|
77
|
+
raise ValueError(f"lookback must be positive, got {self.lookback}")
|
|
78
|
+
if self.min_periods <= 0:
|
|
79
|
+
raise ValueError(f"min_periods must be positive, got {self.min_periods}")
|
|
80
|
+
if self.min_periods > self.lookback:
|
|
81
|
+
raise ValueError(
|
|
82
|
+
f"min_periods ({self.min_periods}) cannot exceed lookback ({self.lookback})"
|
|
83
|
+
)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "cdx_etf_spread_diff",
|
|
4
|
+
"description": "CDX spread minus ETF spread in basis points (raw basis without normalization)",
|
|
5
|
+
"compute_function_name": "compute_cdx_etf_spread_diff",
|
|
6
|
+
"data_requirements": {
|
|
7
|
+
"cdx": "spread",
|
|
8
|
+
"etf": "spread"
|
|
9
|
+
},
|
|
10
|
+
"default_securities": {
|
|
11
|
+
"cdx": "cdx_ig_5y",
|
|
12
|
+
"etf": "lqd"
|
|
13
|
+
},
|
|
14
|
+
"output_units": "basis_points",
|
|
15
|
+
"parameters": {},
|
|
16
|
+
"enabled": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"name": "spread_momentum_5d",
|
|
20
|
+
"description": "5-day spread change in basis points (short-term momentum without volatility adjustment)",
|
|
21
|
+
"compute_function_name": "compute_spread_momentum",
|
|
22
|
+
"data_requirements": {
|
|
23
|
+
"cdx": "spread"
|
|
24
|
+
},
|
|
25
|
+
"default_securities": {
|
|
26
|
+
"cdx": "cdx_ig_5y"
|
|
27
|
+
},
|
|
28
|
+
"output_units": "basis_points",
|
|
29
|
+
"parameters": {
|
|
30
|
+
"lookback": 5
|
|
31
|
+
},
|
|
32
|
+
"enabled": true
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "cdx_vix_deviation_gap_20d",
|
|
36
|
+
"description": "Gap between CDX and VIX deviations from 20-day means (cross-asset risk sentiment)",
|
|
37
|
+
"compute_function_name": "compute_cdx_vix_deviation_gap",
|
|
38
|
+
"data_requirements": {
|
|
39
|
+
"cdx": "spread",
|
|
40
|
+
"vix": "level"
|
|
41
|
+
},
|
|
42
|
+
"default_securities": {
|
|
43
|
+
"cdx": "cdx_ig_5y",
|
|
44
|
+
"vix": "vix"
|
|
45
|
+
},
|
|
46
|
+
"output_units": "basis_points",
|
|
47
|
+
"parameters": {
|
|
48
|
+
"lookback": 20
|
|
49
|
+
},
|
|
50
|
+
"enabled": true
|
|
51
|
+
}
|
|
52
|
+
]
|