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,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
|
+
]
|