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,309 @@
1
+ """
2
+ Core plotting functions for backtest analysis and signal visualization.
3
+
4
+ All functions return Plotly figure objects for flexible rendering
5
+ (Streamlit, Jupyter, HTML export, etc.).
6
+ """
7
+
8
+ import logging
9
+ from typing import Any
10
+
11
+ import pandas as pd
12
+ import plotly.express as px
13
+ import plotly.graph_objects as go
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ def plot_equity_curve(
19
+ pnl: pd.Series,
20
+ title: str = "Cumulative P&L",
21
+ show_drawdown_shading: bool = False,
22
+ ) -> go.Figure:
23
+ """
24
+ Plot cumulative P&L equity curve over time.
25
+
26
+ Parameters
27
+ ----------
28
+ pnl : pd.Series
29
+ Daily P&L series with DatetimeIndex.
30
+ title : str, default "Cumulative P&L"
31
+ Chart title.
32
+ show_drawdown_shading : bool, default False
33
+ If True, shade drawdown regions in red.
34
+
35
+ Returns
36
+ -------
37
+ go.Figure
38
+ Plotly figure object ready for display or export.
39
+
40
+ Notes
41
+ -----
42
+ Cumulative P&L is computed as cumsum of input series.
43
+ Returns interactive chart with hover tooltips and zoom controls.
44
+ """
45
+ logger.info("Plotting equity curve: %d observations", len(pnl))
46
+
47
+ cumulative_pnl = pnl.cumsum()
48
+
49
+ fig = px.line(
50
+ x=cumulative_pnl.index,
51
+ y=cumulative_pnl.values,
52
+ labels={"x": "Date", "y": "Cumulative P&L"},
53
+ title=title,
54
+ )
55
+
56
+ fig.update_traces(line=dict(color="steelblue", width=2))
57
+ fig.update_layout(
58
+ hovermode="x unified",
59
+ template="plotly_white",
60
+ showlegend=False,
61
+ )
62
+
63
+ if show_drawdown_shading:
64
+ running_max = cumulative_pnl.cummax()
65
+ drawdown = cumulative_pnl - running_max
66
+ in_drawdown = drawdown < 0
67
+
68
+ # Add shaded regions for drawdowns
69
+ for start_idx in range(len(in_drawdown)):
70
+ if in_drawdown.iloc[start_idx] and (
71
+ start_idx == 0 or not in_drawdown.iloc[start_idx - 1]
72
+ ):
73
+ # Find end of drawdown period
74
+ end_idx = start_idx
75
+ while end_idx < len(in_drawdown) and in_drawdown.iloc[end_idx]:
76
+ end_idx += 1
77
+
78
+ fig.add_vrect(
79
+ x0=cumulative_pnl.index[start_idx],
80
+ x1=cumulative_pnl.index[min(end_idx, len(in_drawdown) - 1)],
81
+ fillcolor="red",
82
+ opacity=0.1,
83
+ layer="below",
84
+ line_width=0,
85
+ )
86
+
87
+ logger.debug("Equity curve plot generated successfully")
88
+ return fig
89
+
90
+
91
+ def plot_signal(
92
+ signal: pd.Series,
93
+ title: str | None = None,
94
+ threshold_lines: list[float] | None = None,
95
+ ) -> go.Figure:
96
+ """
97
+ Plot time series of a single signal (typically z-score normalized).
98
+
99
+ Parameters
100
+ ----------
101
+ signal : pd.Series
102
+ Signal values with DatetimeIndex.
103
+ title : str | None
104
+ Chart title. Defaults to signal name if available.
105
+ threshold_lines : list[float] | None
106
+ Horizontal reference lines (e.g., [-2, 2] for z-score thresholds).
107
+
108
+ Returns
109
+ -------
110
+ go.Figure
111
+ Plotly figure object with signal trace and optional threshold lines.
112
+
113
+ Notes
114
+ -----
115
+ Designed for z-score normalized signals with typical ranges [-3, 3].
116
+ Use threshold_lines to mark regime boundaries or trading rules.
117
+ """
118
+ logger.info("Plotting signal: %d observations", len(signal))
119
+
120
+ if title is None:
121
+ title = getattr(signal, "name", "Signal")
122
+
123
+ fig = px.line(
124
+ x=signal.index,
125
+ y=signal.values,
126
+ labels={"x": "Date", "y": "Signal Value"},
127
+ title=title,
128
+ )
129
+
130
+ fig.update_traces(line=dict(color="darkorange", width=1.5))
131
+ fig.update_layout(
132
+ hovermode="x unified",
133
+ template="plotly_white",
134
+ showlegend=False,
135
+ )
136
+
137
+ # Add zero line
138
+ fig.add_hline(y=0, line_dash="dash", line_color="gray", opacity=0.5)
139
+
140
+ # Add threshold lines if specified
141
+ if threshold_lines:
142
+ for threshold in threshold_lines:
143
+ fig.add_hline(
144
+ y=threshold,
145
+ line_dash="dot",
146
+ line_color="red",
147
+ opacity=0.4,
148
+ annotation_text=f"±{abs(threshold)}",
149
+ )
150
+
151
+ logger.debug("Signal plot generated successfully")
152
+ return fig
153
+
154
+
155
+ def plot_drawdown(
156
+ pnl: pd.Series,
157
+ title: str = "Drawdown",
158
+ show_underwater_chart: bool = True,
159
+ ) -> go.Figure:
160
+ """
161
+ Plot drawdown curve over time (peak-to-trough decline).
162
+
163
+ Parameters
164
+ ----------
165
+ pnl : pd.Series
166
+ Daily P&L series with DatetimeIndex.
167
+ title : str, default "Drawdown"
168
+ Chart title.
169
+ show_underwater_chart : bool, default True
170
+ If True, displays as underwater equity chart (negative values).
171
+ If False, displays as percentage decline from peak.
172
+
173
+ Returns
174
+ -------
175
+ go.Figure
176
+ Plotly figure object showing drawdown evolution.
177
+
178
+ Notes
179
+ -----
180
+ Drawdown is computed as current cumulative P&L minus running maximum.
181
+ Always non-positive (zero at peaks, negative in drawdown).
182
+ """
183
+ logger.info("Plotting drawdown: %d observations", len(pnl))
184
+
185
+ cumulative_pnl = pnl.cumsum()
186
+ running_max = cumulative_pnl.cummax()
187
+ drawdown = cumulative_pnl - running_max
188
+
189
+ if not show_underwater_chart:
190
+ # Convert to percentage decline
191
+ drawdown = (drawdown / running_max.replace(0, 1)) * 100
192
+
193
+ fig = px.area(
194
+ x=drawdown.index,
195
+ y=drawdown.values,
196
+ labels={
197
+ "x": "Date",
198
+ "y": "Drawdown (%)" if not show_underwater_chart else "Drawdown",
199
+ },
200
+ title=title,
201
+ )
202
+
203
+ fig.update_traces(
204
+ line=dict(color="crimson", width=1),
205
+ fillcolor="rgba(220, 20, 60, 0.3)",
206
+ )
207
+
208
+ fig.update_layout(
209
+ hovermode="x unified",
210
+ template="plotly_white",
211
+ showlegend=False,
212
+ )
213
+
214
+ # Add zero line
215
+ fig.add_hline(y=0, line_dash="solid", line_color="black", opacity=0.3)
216
+
217
+ max_dd = drawdown.min()
218
+ logger.debug("Drawdown plot generated: max_dd=%.2f", max_dd)
219
+ return fig
220
+
221
+
222
+ def plot_attribution(
223
+ signal_contributions: pd.DataFrame,
224
+ title: str = "Signal Attribution",
225
+ ) -> go.Figure:
226
+ """
227
+ Plot signal-level P&L attribution over time.
228
+
229
+ Parameters
230
+ ----------
231
+ signal_contributions : pd.DataFrame
232
+ DatetimeIndex with columns for each signal's P&L contribution.
233
+ title : str, default "Signal Attribution"
234
+ Chart title.
235
+
236
+ Returns
237
+ -------
238
+ go.Figure
239
+ Plotly figure object with stacked area chart.
240
+
241
+ Implementation Status
242
+ ---------------------
243
+ Not implemented — raises NotImplementedError.
244
+
245
+ Notes
246
+ -----
247
+ Placeholder for future implementation.
248
+ Intended for decomposing composite strategy P&L by signal.
249
+ """
250
+ raise NotImplementedError("Signal attribution plotting not yet implemented")
251
+
252
+
253
+ def plot_exposures(
254
+ positions: pd.DataFrame,
255
+ title: str = "Position Exposures",
256
+ ) -> go.Figure:
257
+ """
258
+ Plot strategy exposures over time (notional, delta, etc.).
259
+
260
+ Parameters
261
+ ----------
262
+ positions : pd.DataFrame
263
+ DatetimeIndex with columns for exposure metrics.
264
+ title : str, default "Position Exposures"
265
+ Chart title.
266
+
267
+ Returns
268
+ -------
269
+ go.Figure
270
+ Plotly figure object with multi-line chart.
271
+
272
+ Implementation Status
273
+ ---------------------
274
+ Not implemented — raises NotImplementedError.
275
+
276
+ Notes
277
+ -----
278
+ Placeholder for future implementation.
279
+ Intended for risk management and position monitoring.
280
+ """
281
+ raise NotImplementedError("Exposure plotting not yet implemented")
282
+
283
+
284
+ def plot_dashboard(
285
+ backtest_results: dict[str, Any],
286
+ ) -> go.Figure:
287
+ """
288
+ Generate comprehensive multi-panel dashboard.
289
+
290
+ Parameters
291
+ ----------
292
+ backtest_results : dict[str, Any]
293
+ Dictionary containing P&L, signals, positions, and metrics.
294
+
295
+ Returns
296
+ -------
297
+ go.Figure
298
+ Plotly figure with subplots (equity, drawdown, signals, exposures).
299
+
300
+ Implementation Status
301
+ ---------------------
302
+ Not implemented — raises NotImplementedError.
303
+
304
+ Notes
305
+ -----
306
+ Placeholder for future implementation.
307
+ Intended for integrated view of all backtest outputs.
308
+ """
309
+ raise NotImplementedError("Dashboard plotting not yet implemented")
@@ -0,0 +1,242 @@
1
+ """
2
+ Visualizer class wrapping plotting functions for extensibility.
3
+
4
+ Provides object-oriented interface for future enhancements like
5
+ theming, caching, export utilities, and Streamlit integration.
6
+ """
7
+
8
+ import logging
9
+ from typing import Any
10
+
11
+ import pandas as pd
12
+ import plotly.graph_objects as go
13
+
14
+ from .plots import (
15
+ plot_attribution,
16
+ plot_dashboard,
17
+ plot_drawdown,
18
+ plot_equity_curve,
19
+ plot_exposures,
20
+ plot_signal,
21
+ )
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ class Visualizer:
27
+ """
28
+ Wrapper class for visualization functions with extensibility hooks.
29
+
30
+ Provides unified interface for all plotting operations and future
31
+ enhancements like custom themes, batch exports, or caching.
32
+
33
+ Parameters
34
+ ----------
35
+ theme : str, default "plotly_white"
36
+ Plotly template name for consistent styling.
37
+ export_path : str | None
38
+ Optional directory for automatic HTML/PNG export.
39
+
40
+ Examples
41
+ --------
42
+ >>> viz = Visualizer()
43
+ >>> fig = viz.equity_curve(daily_pnl)
44
+ >>> fig.show()
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ theme: str = "plotly_white",
50
+ export_path: str | None = None,
51
+ ) -> None:
52
+ """Initialize visualizer with configuration."""
53
+ self.theme = theme
54
+ self.export_path = export_path
55
+ logger.info("Visualizer initialized: theme=%s", theme)
56
+
57
+ def equity_curve(
58
+ self,
59
+ pnl: pd.Series,
60
+ title: str = "Cumulative P&L",
61
+ show_drawdown_shading: bool = False,
62
+ ) -> go.Figure:
63
+ """
64
+ Plot cumulative P&L equity curve.
65
+
66
+ Delegates to plot_equity_curve with instance configuration.
67
+
68
+ Parameters
69
+ ----------
70
+ pnl : pd.Series
71
+ Daily P&L series with DatetimeIndex.
72
+ title : str, default "Cumulative P&L"
73
+ Chart title.
74
+ show_drawdown_shading : bool, default False
75
+ If True, shade drawdown regions.
76
+
77
+ Returns
78
+ -------
79
+ go.Figure
80
+ Plotly figure object.
81
+ """
82
+ fig = plot_equity_curve(pnl, title, show_drawdown_shading)
83
+ fig.update_layout(template=self.theme)
84
+ self._maybe_export(fig, "equity_curve")
85
+ return fig
86
+
87
+ def signal(
88
+ self,
89
+ signal: pd.Series,
90
+ title: str | None = None,
91
+ threshold_lines: list[float] | None = None,
92
+ ) -> go.Figure:
93
+ """
94
+ Plot time series of a signal.
95
+
96
+ Delegates to plot_signal with instance configuration.
97
+
98
+ Parameters
99
+ ----------
100
+ signal : pd.Series
101
+ Signal values with DatetimeIndex.
102
+ title : str | None
103
+ Chart title.
104
+ threshold_lines : list[float] | None
105
+ Horizontal reference lines.
106
+
107
+ Returns
108
+ -------
109
+ go.Figure
110
+ Plotly figure object.
111
+ """
112
+ fig = plot_signal(signal, title, threshold_lines)
113
+ fig.update_layout(template=self.theme)
114
+ self._maybe_export(fig, "signal")
115
+ return fig
116
+
117
+ def drawdown(
118
+ self,
119
+ pnl: pd.Series,
120
+ title: str = "Drawdown",
121
+ show_underwater_chart: bool = True,
122
+ ) -> go.Figure:
123
+ """
124
+ Plot drawdown curve.
125
+
126
+ Delegates to plot_drawdown with instance configuration.
127
+
128
+ Parameters
129
+ ----------
130
+ pnl : pd.Series
131
+ Daily P&L series with DatetimeIndex.
132
+ title : str, default "Drawdown"
133
+ Chart title.
134
+ show_underwater_chart : bool, default True
135
+ If True, displays as underwater chart.
136
+
137
+ Returns
138
+ -------
139
+ go.Figure
140
+ Plotly figure object.
141
+ """
142
+ fig = plot_drawdown(pnl, title, show_underwater_chart)
143
+ fig.update_layout(template=self.theme)
144
+ self._maybe_export(fig, "drawdown")
145
+ return fig
146
+
147
+ def attribution(
148
+ self,
149
+ signal_contributions: pd.DataFrame,
150
+ title: str = "Signal Attribution",
151
+ ) -> go.Figure:
152
+ """
153
+ Plot signal-level P&L attribution (placeholder).
154
+
155
+ Parameters
156
+ ----------
157
+ signal_contributions : pd.DataFrame
158
+ DatetimeIndex with columns for each signal's P&L.
159
+ title : str, default "Signal Attribution"
160
+ Chart title.
161
+
162
+ Returns
163
+ -------
164
+ go.Figure
165
+ Plotly figure object.
166
+
167
+ Raises
168
+ ------
169
+ NotImplementedError
170
+ Feature not yet implemented.
171
+ """
172
+ return plot_attribution(signal_contributions, title)
173
+
174
+ def exposures(
175
+ self,
176
+ positions: pd.DataFrame,
177
+ title: str = "Position Exposures",
178
+ ) -> go.Figure:
179
+ """
180
+ Plot strategy exposures over time (placeholder).
181
+
182
+ Parameters
183
+ ----------
184
+ positions : pd.DataFrame
185
+ DatetimeIndex with exposure metrics.
186
+ title : str, default "Position Exposures"
187
+ Chart title.
188
+
189
+ Returns
190
+ -------
191
+ go.Figure
192
+ Plotly figure object.
193
+
194
+ Raises
195
+ ------
196
+ NotImplementedError
197
+ Feature not yet implemented.
198
+ """
199
+ return plot_exposures(positions, title)
200
+
201
+ def dashboard(
202
+ self,
203
+ backtest_results: dict[str, Any],
204
+ ) -> go.Figure:
205
+ """
206
+ Generate comprehensive multi-panel dashboard (placeholder).
207
+
208
+ Parameters
209
+ ----------
210
+ backtest_results : dict[str, Any]
211
+ Dictionary with P&L, signals, positions, and metrics.
212
+
213
+ Returns
214
+ -------
215
+ go.Figure
216
+ Plotly figure with subplots.
217
+
218
+ Raises
219
+ ------
220
+ NotImplementedError
221
+ Feature not yet implemented.
222
+ """
223
+ return plot_dashboard(backtest_results)
224
+
225
+ def _maybe_export(self, fig: go.Figure, name: str) -> None:
226
+ """
227
+ Export figure to HTML if export_path is configured.
228
+
229
+ Parameters
230
+ ----------
231
+ fig : go.Figure
232
+ Plotly figure to export.
233
+ name : str
234
+ Base filename (without extension).
235
+
236
+ Notes
237
+ -----
238
+ Future enhancement for batch export and archival.
239
+ """
240
+ if self.export_path is not None:
241
+ # Placeholder for export logic
242
+ logger.debug("Export path configured but not yet implemented: %s", name)
@@ -0,0 +1,18 @@
1
+ """
2
+ Workflow orchestration for systematic macro credit research.
3
+
4
+ Provides infrastructure for executing multi-step research pipelines
5
+ with dependency tracking, caching, and error handling.
6
+ """
7
+
8
+ from .config import WorkflowConfig
9
+ from .engine import WorkflowEngine
10
+ from .steps import WorkflowStep
11
+ from .registry import StepRegistry
12
+
13
+ __all__ = [
14
+ "WorkflowConfig",
15
+ "WorkflowEngine",
16
+ "WorkflowStep",
17
+ "StepRegistry",
18
+ ]