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,455 @@
|
|
|
1
|
+
# CDX Knowledge Base
|
|
2
|
+
|
|
3
|
+
> Comprehensive reference for Credit Default Index Swaps (CDX) trading and P&L calculation
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [What is CDX?](#what-is-cdx)
|
|
10
|
+
2. [CDX Index Family](#cdx-index-family)
|
|
11
|
+
3. [Key Terminology](#key-terminology)
|
|
12
|
+
4. [DV01 and Risk Metrics](#dv01-and-risk-metrics)
|
|
13
|
+
5. [P&L Calculation](#pnl-calculation)
|
|
14
|
+
6. [Transaction Costs](#transaction-costs)
|
|
15
|
+
7. [Trading Conventions](#trading-conventions)
|
|
16
|
+
8. [Signal Interpretation](#signal-interpretation)
|
|
17
|
+
9. [Project Implementation](#project-implementation)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## What is CDX?
|
|
22
|
+
|
|
23
|
+
**Credit Default Index Swaps (CDX)** are tradeable credit derivative indices that reference a basket of single-name credit default swaps (CDS). They provide standardized exposure to corporate credit risk in North American markets.
|
|
24
|
+
|
|
25
|
+
### How CDX Works
|
|
26
|
+
|
|
27
|
+
1. **Protection Buyer**: Pays periodic premium (spread) to protection seller
|
|
28
|
+
2. **Protection Seller**: Receives spread, pays out if credit event occurs
|
|
29
|
+
3. **Credit Events**: Bankruptcy, failure to pay, restructuring (for some contracts)
|
|
30
|
+
|
|
31
|
+
### Why Trade CDX?
|
|
32
|
+
|
|
33
|
+
- **Liquidity**: Most liquid credit derivatives (tight bid-ask spreads)
|
|
34
|
+
- **Standardization**: Fixed composition, maturity, coupon
|
|
35
|
+
- **Capital Efficiency**: Margin-based trading, no upfront principal
|
|
36
|
+
- **Hedging**: Efficiently hedge credit portfolio risk
|
|
37
|
+
- **Speculation**: Express directional views on credit markets
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## CDX Index Family
|
|
42
|
+
|
|
43
|
+
### North American Indices
|
|
44
|
+
|
|
45
|
+
| Index | Description | Typical Spread Range | DV01 per $1MM |
|
|
46
|
+
|-------|-------------|---------------------|---------------|
|
|
47
|
+
| **CDX.NA.IG** | 125 Investment Grade names | 50-150 bps | ~$450-520 |
|
|
48
|
+
| **CDX.NA.HY** | 100 High Yield names | 300-600 bps | ~$350-450 |
|
|
49
|
+
| **CDX.NA.XO** | Crossover (BB-rated) | 200-400 bps | ~$400-480 |
|
|
50
|
+
|
|
51
|
+
### Tenors
|
|
52
|
+
|
|
53
|
+
- **5-Year**: Most liquid, primary trading vehicle
|
|
54
|
+
- **10-Year**: Less liquid, used for curve trades
|
|
55
|
+
- **3-Year, 7-Year**: Available but limited liquidity
|
|
56
|
+
|
|
57
|
+
### Index Rolls
|
|
58
|
+
|
|
59
|
+
- CDX indices roll **every 6 months** (March and September)
|
|
60
|
+
- New series issued with updated constituent list
|
|
61
|
+
- On-the-run series has best liquidity
|
|
62
|
+
- Off-the-run series trade at discount
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Key Terminology
|
|
67
|
+
|
|
68
|
+
### Spread
|
|
69
|
+
|
|
70
|
+
The **spread** is the annual premium paid by the protection buyer, quoted in basis points (bps).
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
Spread = 100 bps means paying 1% per year on notional
|
|
74
|
+
For $10MM notional: $100,000/year = ~$274/day
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Spread Movements
|
|
78
|
+
|
|
79
|
+
| Movement | What It Means | Market Sentiment |
|
|
80
|
+
|----------|---------------|------------------|
|
|
81
|
+
| **Spreads Widen** | Credit risk increasing | Risk-off, bearish credit |
|
|
82
|
+
| **Spreads Tighten** | Credit risk decreasing | Risk-on, bullish credit |
|
|
83
|
+
|
|
84
|
+
### Price vs. Spread
|
|
85
|
+
|
|
86
|
+
CDX can be quoted as **price** (like a bond) or **spread** (like CDS):
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
Price ≈ 100 - (Spread × Duration)
|
|
90
|
+
|
|
91
|
+
Example: If spread = 100 bps and duration = 5 years
|
|
92
|
+
Price ≈ 100 - (1.00 × 5) = 95
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Fixed Coupon
|
|
96
|
+
|
|
97
|
+
Modern CDX contracts have **fixed coupons**:
|
|
98
|
+
- **CDX IG**: 100 bps fixed coupon
|
|
99
|
+
- **CDX HY**: 500 bps fixed coupon
|
|
100
|
+
|
|
101
|
+
The difference between market spread and fixed coupon is settled upfront.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## DV01 and Risk Metrics
|
|
106
|
+
|
|
107
|
+
### What is DV01?
|
|
108
|
+
|
|
109
|
+
**DV01** (Dollar Value of 01) is the P&L impact of a 1 basis point move in spreads.
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
DV01 = Notional × Duration × 0.0001
|
|
113
|
+
|
|
114
|
+
For CDX IG 5Y with ~4.75 year duration:
|
|
115
|
+
DV01 per $1MM = $1,000,000 × 4.75 × 0.0001 = $475
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Typical DV01 Values
|
|
119
|
+
|
|
120
|
+
| Index | Tenor | DV01 per $1MM | Notes |
|
|
121
|
+
|-------|-------|---------------|-------|
|
|
122
|
+
| CDX IG | 5Y | ~$475 | Primary trading vehicle |
|
|
123
|
+
| CDX IG | 10Y | ~$850 | Higher duration exposure |
|
|
124
|
+
| CDX HY | 5Y | ~$400 | Lower due to wider spreads |
|
|
125
|
+
|
|
126
|
+
### DV01 in Aponyx
|
|
127
|
+
|
|
128
|
+
The project uses `dv01_per_million = 475.0` in the strategy catalog, representing the actual DV01 per $1MM for CDX IG 5Y:
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
// strategy_catalog.json
|
|
132
|
+
{
|
|
133
|
+
"name": "balanced",
|
|
134
|
+
"dv01_per_million": 475.0, // $475 per bp per $1MM
|
|
135
|
+
...
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
BacktestConfig reads DV01 from the strategy catalog (no hardcoded default).
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## P&L Calculation
|
|
144
|
+
|
|
145
|
+
### Core P&L Formula
|
|
146
|
+
|
|
147
|
+
For a **long credit risk position** (sold protection / bought CDX):
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
Daily P&L = -Position × ΔSpread × DV01 × Notional_MM
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Where:
|
|
154
|
+
- **Position**: +1 (long credit) or -1 (short credit)
|
|
155
|
+
- **ΔSpread**: Today's spread - Yesterday's spread (in bps)
|
|
156
|
+
- **DV01**: Dollar value per basis point per $1MM (~$475 for CDX IG 5Y)
|
|
157
|
+
- **Notional_MM**: Position size in millions (e.g., 10 for $10MM)
|
|
158
|
+
|
|
159
|
+
### Why the Negative Sign?
|
|
160
|
+
|
|
161
|
+
**Long credit risk** means you **profit when spreads tighten**:
|
|
162
|
+
- Spreads tighten (ΔSpread < 0) → P&L > 0 (profit)
|
|
163
|
+
- Spreads widen (ΔSpread > 0) → P&L < 0 (loss)
|
|
164
|
+
|
|
165
|
+
The negative sign converts spread direction to profit direction.
|
|
166
|
+
|
|
167
|
+
### P&L Examples
|
|
168
|
+
|
|
169
|
+
**Example 1: Long Position, Spreads Tighten**
|
|
170
|
+
```
|
|
171
|
+
Position: Long $10MM CDX IG 5Y
|
|
172
|
+
Yesterday's spread: 100 bps
|
|
173
|
+
Today's spread: 98 bps
|
|
174
|
+
ΔSpread: -2 bps
|
|
175
|
+
DV01: $475 per $1MM
|
|
176
|
+
|
|
177
|
+
P&L = -1 × (-2) × 475 × 10
|
|
178
|
+
P&L = +$9,500 (profit)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Example 2: Long Position, Spreads Widen**
|
|
182
|
+
```
|
|
183
|
+
Position: Long $10MM CDX IG 5Y
|
|
184
|
+
Yesterday's spread: 100 bps
|
|
185
|
+
Today's spread: 103 bps
|
|
186
|
+
ΔSpread: +3 bps
|
|
187
|
+
|
|
188
|
+
P&L = -1 × (+3) × 475 × 10
|
|
189
|
+
P&L = -$14,250 (loss)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Example 3: Short Position, Spreads Widen**
|
|
193
|
+
```
|
|
194
|
+
Position: Short $10MM CDX IG 5Y
|
|
195
|
+
Yesterday's spread: 100 bps
|
|
196
|
+
Today's spread: 105 bps
|
|
197
|
+
ΔSpread: +5 bps
|
|
198
|
+
|
|
199
|
+
P&L = -(-1) × (+5) × 475 × 10
|
|
200
|
+
P&L = +$23,750 (profit)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Implementation in Aponyx
|
|
204
|
+
|
|
205
|
+
From `src/aponyx/backtest/engine.py`:
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
if abs(position_before_update) > 1e-9:
|
|
209
|
+
spread_change = spread_level - prev_spread_before_update
|
|
210
|
+
if is_proportional:
|
|
211
|
+
# Proportional: position_before_update is actual notional in MM
|
|
212
|
+
spread_pnl = (
|
|
213
|
+
-np.sign(position_before_update)
|
|
214
|
+
* abs(position_before_update)
|
|
215
|
+
* spread_change
|
|
216
|
+
* config.dv01_per_million
|
|
217
|
+
)
|
|
218
|
+
else:
|
|
219
|
+
# Binary: position_before_update is direction indicator (+1/-1)
|
|
220
|
+
spread_pnl = (
|
|
221
|
+
-position_before_update
|
|
222
|
+
* spread_change
|
|
223
|
+
* config.dv01_per_million
|
|
224
|
+
* config.position_size_mm
|
|
225
|
+
)
|
|
226
|
+
else:
|
|
227
|
+
spread_pnl = 0.0
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Transaction Costs
|
|
233
|
+
|
|
234
|
+
### Components of CDX Trading Costs
|
|
235
|
+
|
|
236
|
+
1. **Bid-Ask Spread**: Primary cost, ~0.5-2 bps for CDX IG
|
|
237
|
+
2. **Clearing Fees**: CME/ICE clearing costs
|
|
238
|
+
3. **Commission**: Broker fees
|
|
239
|
+
4. **Market Impact**: For large trades
|
|
240
|
+
|
|
241
|
+
### Typical Transaction Costs
|
|
242
|
+
|
|
243
|
+
| Index | Bid-Ask (bps) | Round-Trip Cost |
|
|
244
|
+
|-------|---------------|-----------------|
|
|
245
|
+
| CDX IG 5Y | 0.5-1.0 | 1-2 bps |
|
|
246
|
+
| CDX IG 10Y | 1.0-2.0 | 2-4 bps |
|
|
247
|
+
| CDX HY 5Y | 2.0-4.0 | 4-8 bps |
|
|
248
|
+
|
|
249
|
+
### Cost Calculation in Aponyx
|
|
250
|
+
|
|
251
|
+
Transaction costs depend on sizing mode:
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
# Binary mode: cost based on full position_size_mm
|
|
255
|
+
cost = transaction_cost_bps * position_size_mm * 100
|
|
256
|
+
|
|
257
|
+
# For $10MM position with 1 bps cost:
|
|
258
|
+
cost = 1.0 * 10.0 * 100 = $1,000 per entry/exit
|
|
259
|
+
|
|
260
|
+
# Proportional mode: cost based on actual traded notional
|
|
261
|
+
cost = abs(position_notional_mm) * transaction_cost_bps * 100
|
|
262
|
+
|
|
263
|
+
# For $5MM actual position with 1 bps cost:
|
|
264
|
+
cost = 5.0 * 1.0 * 100 = $500 per entry/exit
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Note: Costs are applied on **each** entry and exit, not round-trip.
|
|
268
|
+
|
|
269
|
+
### Implementation
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
@dataclass(frozen=True)
|
|
273
|
+
class BacktestConfig:
|
|
274
|
+
transaction_cost_bps: float # Per-trade cost (entry OR exit)
|
|
275
|
+
|
|
276
|
+
# Binary mode
|
|
277
|
+
entry_cost = config.transaction_cost_bps * config.position_size_mm * 100
|
|
278
|
+
exit_cost = config.transaction_cost_bps * config.position_size_mm * 100
|
|
279
|
+
|
|
280
|
+
# Proportional mode
|
|
281
|
+
entry_cost = abs(current_position) * config.transaction_cost_bps * 100
|
|
282
|
+
exit_cost = abs(current_position) * config.transaction_cost_bps * 100
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Trading Conventions
|
|
288
|
+
|
|
289
|
+
### Position Direction
|
|
290
|
+
|
|
291
|
+
| Action | Position | Credit Risk | Profit When |
|
|
292
|
+
|--------|----------|-------------|-------------|
|
|
293
|
+
| **Buy CDX** | Long | Long credit risk | Spreads tighten |
|
|
294
|
+
| **Sell CDX** | Short | Short credit risk | Spreads widen |
|
|
295
|
+
| **Sell Protection** | Long | Long credit risk | Spreads tighten |
|
|
296
|
+
| **Buy Protection** | Short | Short credit risk | Spreads widen |
|
|
297
|
+
|
|
298
|
+
### Market Quoting
|
|
299
|
+
|
|
300
|
+
CDX is typically quoted as spread in basis points:
|
|
301
|
+
- "CDX IG is offered at 75" means 75 bps spread
|
|
302
|
+
- "CDX IG traded 2 tighter" means spread narrowed by 2 bps
|
|
303
|
+
|
|
304
|
+
### Settlement
|
|
305
|
+
|
|
306
|
+
- **T+1 settlement** for standard trades
|
|
307
|
+
- **Central clearing** required (CME, ICE)
|
|
308
|
+
- **Daily margin** based on mark-to-market
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Signal Interpretation
|
|
313
|
+
|
|
314
|
+
### Aponyx Signal Convention
|
|
315
|
+
|
|
316
|
+
**CRITICAL**: All signals in Aponyx follow this convention:
|
|
317
|
+
- **Positive signal** → Long credit risk (buy CDX / sell protection)
|
|
318
|
+
- **Negative signal** → Short credit risk (sell CDX / buy protection)
|
|
319
|
+
|
|
320
|
+
### Signal Examples
|
|
321
|
+
|
|
322
|
+
| Signal | Signal Value | Position | Expectation |
|
|
323
|
+
|--------|--------------|----------|-------------|
|
|
324
|
+
| CDX-ETF Basis | +2.0 | Long | CDX cheap, expect tightening |
|
|
325
|
+
| CDX-ETF Basis | -1.5 | Short | CDX expensive, expect widening |
|
|
326
|
+
| Spread Momentum | +1.2 | Long | Tightening momentum continues |
|
|
327
|
+
| CDX-VIX Gap | -0.8 | Short | Credit stress exceeds equity stress |
|
|
328
|
+
|
|
329
|
+
### Signal-to-Position Logic
|
|
330
|
+
|
|
331
|
+
From the backtest engine:
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
# Non-zero signal = enter position
|
|
335
|
+
if not signal_is_zero:
|
|
336
|
+
if is_proportional:
|
|
337
|
+
target_position = signal_val * config.position_size_mm
|
|
338
|
+
else: # Binary mode
|
|
339
|
+
target_position = 1.0 if signal_val > 0 else -1.0
|
|
340
|
+
|
|
341
|
+
# Zero signal = exit position
|
|
342
|
+
if signal_is_zero:
|
|
343
|
+
exit_reason = "signal"
|
|
344
|
+
current_position = 0.0
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Project Implementation
|
|
350
|
+
|
|
351
|
+
### Key Files
|
|
352
|
+
|
|
353
|
+
| File | Purpose |
|
|
354
|
+
|------|---------|
|
|
355
|
+
| `backtest/engine.py` | P&L calculation, position management |
|
|
356
|
+
| `backtest/config.py` | BacktestConfig with DV01, costs |
|
|
357
|
+
| `backtest/strategy_catalog.json` | Pre-configured strategies |
|
|
358
|
+
| `docs/cdx_overlay_strategy.md` | Strategy design document |
|
|
359
|
+
|
|
360
|
+
### BacktestConfig Parameters
|
|
361
|
+
|
|
362
|
+
```python
|
|
363
|
+
@dataclass(frozen=True)
|
|
364
|
+
class BacktestConfig:
|
|
365
|
+
position_size_mm: float # Notional in $MM (required)
|
|
366
|
+
sizing_mode: str # "proportional" or "binary" (required)
|
|
367
|
+
stop_loss_pct: float | None # % of position value
|
|
368
|
+
take_profit_pct: float | None # % of position value
|
|
369
|
+
max_holding_days: int | None # Forced exit
|
|
370
|
+
transaction_cost_bps: float # Per-trade cost in bps (required)
|
|
371
|
+
dv01_per_million: float # DV01 per $1MM (required, ~475 for CDX IG 5Y)
|
|
372
|
+
signal_lag: int = 1 # Days to lag signal (0=same-day, 1=next-day)
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Note: All parameters except `signal_lag` must be explicitly provided. Use `StrategyRegistry.get()` to load from catalog.
|
|
376
|
+
|
|
377
|
+
### Strategy Catalog
|
|
378
|
+
|
|
379
|
+
```json
|
|
380
|
+
{
|
|
381
|
+
"name": "balanced",
|
|
382
|
+
"description": "Balanced position sizing with moderate risk management",
|
|
383
|
+
"position_size_mm": 10.0,
|
|
384
|
+
"sizing_mode": "proportional",
|
|
385
|
+
"stop_loss_pct": 5.0,
|
|
386
|
+
"take_profit_pct": 10.0,
|
|
387
|
+
"max_holding_days": null,
|
|
388
|
+
"transaction_cost_bps": 1.0,
|
|
389
|
+
"dv01_per_million": 475.0,
|
|
390
|
+
"enabled": true
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Note: `signal_lag` defaults to 1 in BacktestConfig and is not stored in the catalog.
|
|
395
|
+
|
|
396
|
+
### Running a Backtest
|
|
397
|
+
|
|
398
|
+
```python
|
|
399
|
+
from aponyx.backtest import BacktestConfig, run_backtest
|
|
400
|
+
|
|
401
|
+
# DV01 comes from strategy catalog (475.0 for CDX IG 5Y)
|
|
402
|
+
config = BacktestConfig(
|
|
403
|
+
position_size_mm=10.0,
|
|
404
|
+
stop_loss_pct=5.0,
|
|
405
|
+
dv01_per_million=475.0
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
result = run_backtest(signal, spread, config)
|
|
409
|
+
|
|
410
|
+
# Access results
|
|
411
|
+
print(f"Total P&L: ${result.pnl['cumulative_pnl'].iloc[-1]:,.0f}")
|
|
412
|
+
print(f"Number of trades: {result.metadata['summary']['n_trades']}")
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
## Quick Reference Card
|
|
418
|
+
|
|
419
|
+
### P&L Formula
|
|
420
|
+
```
|
|
421
|
+
Daily P&L = -Position × ΔSpread × DV01 × Notional_MM
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### DV01 Approximation
|
|
425
|
+
```
|
|
426
|
+
DV01 per $1MM ≈ Duration × $100
|
|
427
|
+
|
|
428
|
+
CDX IG 5Y: ~4.75 × 100 = ~$475/bp per $1MM
|
|
429
|
+
For $10MM position: $475 × 10 = $4,750/bp
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### Position Mapping
|
|
433
|
+
```
|
|
434
|
+
Positive Signal → Long Credit → Buy CDX → Profit on Tightening
|
|
435
|
+
Negative Signal → Short Credit → Sell CDX → Profit on Widening
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### Cost Impact
|
|
439
|
+
```
|
|
440
|
+
Break-even move = Entry Cost (bps) + Exit Cost (bps)
|
|
441
|
+
With 1 bps per trade: need 2 bps total move to break even (1 entry + 1 exit)
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
## References
|
|
447
|
+
|
|
448
|
+
- S&P Global: CDS Indices Primer
|
|
449
|
+
- IHS Markit: CDX Index Methodology
|
|
450
|
+
- CME Group: CDX Index Futures Specifications
|
|
451
|
+
- Aponyx Strategy Design: `docs/cdx_overlay_strategy.md`
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
*Last Updated: December 2025*
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Pilot Systematic CDX Overlay Strategy
|
|
2
|
+
|
|
3
|
+
## 1. Objective and Role
|
|
4
|
+
|
|
5
|
+
**Investment Objective:**
|
|
6
|
+
The pilot aims to generate short-term tactical alpha by exploiting temporary dislocations in liquid credit indices, while also providing a liquidity hedge and modest convexity enhancement.
|
|
7
|
+
|
|
8
|
+
**Relationship to Core Allocation:**
|
|
9
|
+
This overlay is faster-reacting, capital-efficient, and designed to be largely uncorrelated with the slower-moving core credit allocation. It serves as a tactical enhancer rather than a strategic allocation change.
|
|
10
|
+
|
|
11
|
+
**Investment Horizon & Turnover:**
|
|
12
|
+
Typical holding period is **days to a few weeks**, with moderate turnover aligned to signal persistence. The strategy emphasizes nimble positioning while maintaining disciplined exposure limits.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 2. Scope of Instruments
|
|
17
|
+
|
|
18
|
+
**Primary Tradeable Instruments:**
|
|
19
|
+
- **CDX Investment Grade (IG)**: 5Y and 10Y tenors
|
|
20
|
+
- **CDX High Yield (HY)**: 5Y tenor
|
|
21
|
+
- **CDX North America ex-IG/XO (XO)**: 5Y tenor
|
|
22
|
+
|
|
23
|
+
**Signal Proxies:**
|
|
24
|
+
- ETFs such as **HYG** and **LQD** may be used for **signal generation only**, not direct trading, to capture market flow and basis information.
|
|
25
|
+
|
|
26
|
+
**Liquidity & Clearing Considerations:**
|
|
27
|
+
- Focus exclusively on indices with tight bid/ask spreads and reliable clearing.
|
|
28
|
+
- Monitor DV01 and margin usage to ensure efficient capital deployment and compliance with clearinghouse requirements.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 3. Core Signals
|
|
33
|
+
|
|
34
|
+
### 3.1 CDX-ETF Basis (`cdx_etf_basis`)
|
|
35
|
+
**Rationale:** Pricing gaps between ETFs and CDX indices can indicate temporary mispricing caused by fund flows or liquidity constraints.
|
|
36
|
+
|
|
37
|
+
**Signal Convention:**
|
|
38
|
+
- **Positive signal** = CDX cheap (wider spreads) → Long credit risk → Buy CDX (sell protection)
|
|
39
|
+
- **Negative signal** = CDX expensive (tighter spreads) → Short credit risk → Sell CDX (buy protection)
|
|
40
|
+
|
|
41
|
+
**Calculation:**
|
|
42
|
+
- Raw calculation: `CDX spread - ETF spread`
|
|
43
|
+
- No sign adjustment needed (natural directionality)
|
|
44
|
+
- Positive values indicate CDX is cheap relative to ETF
|
|
45
|
+
|
|
46
|
+
**Normalization:**
|
|
47
|
+
- Adjust for **ETF flows and volatility**, scaling the basis signal to capture persistent dislocations rather than transient noise.
|
|
48
|
+
|
|
49
|
+
### 3.2 CDX-VIX Gap (`cdx_vix_gap`)
|
|
50
|
+
**Rationale:** Divergences between equity implied volatility (VIX) and credit spreads reflect asymmetric cross-asset stress conditions.
|
|
51
|
+
|
|
52
|
+
**Signal Convention:**
|
|
53
|
+
- **Positive signal** = Credit stress exceeds equity stress → Long credit risk → Buy CDX (sell protection)
|
|
54
|
+
- **Negative signal** = Equity stress exceeds credit stress → Short credit risk → Sell CDX (buy protection)
|
|
55
|
+
|
|
56
|
+
**Calculation:**
|
|
57
|
+
- Raw calculation: `(CDX deviation from mean) - (VIX deviation from mean)`
|
|
58
|
+
- No sign adjustment needed (natural directionality)
|
|
59
|
+
- Positive values indicate credit-specific stress (CDX spreads spiking relative to VIX)
|
|
60
|
+
|
|
61
|
+
**Considerations:**
|
|
62
|
+
- Differentiate **transient spikes** from sustained regime shifts using short-term averaging and persistence filters.
|
|
63
|
+
|
|
64
|
+
### 3.3 Spread Momentum (`spread_momentum`)
|
|
65
|
+
**Rationale:** CDX spreads exhibit measurable short-horizon momentum or mean-reversion.
|
|
66
|
+
|
|
67
|
+
**Signal Convention:**
|
|
68
|
+
- **Positive signal** = Favorable tightening momentum → Long credit risk → Buy CDX (sell protection)
|
|
69
|
+
- **Negative signal** = Unfavorable widening momentum → Short credit risk → Sell CDX (buy protection)
|
|
70
|
+
|
|
71
|
+
**Calculation:**
|
|
72
|
+
- Raw calculation: `current_spread - past_spread` (positive when widening)
|
|
73
|
+
- **Sign adjustment required**: Negated during calculation because spread widening (positive change) should produce negative signal
|
|
74
|
+
- Formula: `signal = -(spread_change / volatility)`
|
|
75
|
+
- Positive values indicate spreads are tightening (bullish for credit)
|
|
76
|
+
|
|
77
|
+
**Horizon:** Typically **3–10 days**, with signals normalized for recent volatility.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 4. Signal Integration & Positioning Logic
|
|
82
|
+
|
|
83
|
+
**Signal Standardization:**
|
|
84
|
+
- Convert each signal into a **z-score or percentile rank** to allow comparability.
|
|
85
|
+
|
|
86
|
+
**Signal Evaluation:**
|
|
87
|
+
- Each signal is evaluated independently to establish clear performance attribution.
|
|
88
|
+
- Signals can be combined in future experiments once individual performance is understood.
|
|
89
|
+
|
|
90
|
+
**Trade Expression:**
|
|
91
|
+
- **Positive signal:** Long credit risk → Buy CDX (sell protection)
|
|
92
|
+
- **Negative signal:** Short credit risk → Sell CDX (buy protection)
|
|
93
|
+
- **Neutral/reduced exposure:** When signal is weak or below threshold.
|
|
94
|
+
|
|
95
|
+
**Update Frequency:**
|
|
96
|
+
- Signals refreshed **daily**, with expected half-life of **3–7 days** depending on market regime.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 5. Risk, Sizing, and Governance
|
|
101
|
+
|
|
102
|
+
**Position Sizing:**
|
|
103
|
+
- Scale positions by **DV01** or **volatility**, ensuring gross/net risk limits are respected.
|
|
104
|
+
- Typical gross risk: **0.5–2% DV01 of core portfolio**, with net directional limits tighter.
|
|
105
|
+
|
|
106
|
+
**Stop-Loss & Signal Fade:**
|
|
107
|
+
- Reduce or unwind positions when signals reverse or trigger pre-defined stop-loss levels.
|
|
108
|
+
|
|
109
|
+
**Liquidity & Slippage:**
|
|
110
|
+
- Assumes **tight CDX bid/ask spreads**, allowing rapid adjustment with minimal market impact.
|
|
111
|
+
|
|
112
|
+
**Governance:**
|
|
113
|
+
- Clear documentation and approval of positions separate from core allocation team.
|
|
114
|
+
- Regular review ensures overlay remains tactical and does not replace strategic allocations.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 6. Evaluation Framework
|
|
119
|
+
|
|
120
|
+
**Performance Metrics:**
|
|
121
|
+
- Hit rate, **information ratio**, and **turnover-adjusted return**.
|
|
122
|
+
|
|
123
|
+
**Diversification:**
|
|
124
|
+
- Assess contribution to portfolio **risk-adjusted return** and correlation to core positions.
|
|
125
|
+
|
|
126
|
+
**Stress Testing:**
|
|
127
|
+
- Evaluate performance under macro shocks, volatility spikes, and ETF outflows.
|
|
128
|
+
|
|
129
|
+
**Scaling Considerations:**
|
|
130
|
+
- Lessons from pilot inform a **multi-signal production strategy**, including potential expansion of indices, tenors, and signal types.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
This brief frames the pilot as a **capital-efficient, tactical overlay** that exploits temporary mispricings in liquid CDX indices, complements the core portfolio, and has robust risk and governance controls.
|
|
135
|
+
|