-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrun_phase7.py
More file actions
100 lines (87 loc) · 4.17 KB
/
Copy pathrun_phase7.py
File metadata and controls
100 lines (87 loc) · 4.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
"""
run_phase7.py (Phase 7e report)
================================
End-to-end Phase 7 report:
1. Build the real SX5E universe (BL data layer) + archetype ESG profiles
2. Show the PAB exclusion outcome on the real benchmark
3. Run the three compliance scenarios (cost of each path)
4. Compute and plot the cost-of-green frontier
Run: python3 run_phase7.py (pulls live yfinance data; needs network)
Outputs: console report + cost_of_green_frontier.png
"""
from __future__ import annotations
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from phase7.esg_data import build_universe
from phase7.esg_optimiser import (run_scenarios, cost_of_green_frontier,
benchmark_waci)
from sfdr2.exclusions import screen_portfolio
MAX_W = 0.12 # UCITS-style per-name cap; also regularises the optimiser
YEAR = 2026
def main():
print("Building real SX5E universe via the BL data layer...")
b = build_universe(years=3)
tickers = b["tickers"]
print("=" * 70)
print("PHASE 7 - ESG-TILTED MINIMUM-TRACKING-ERROR OPTIMISER")
print("=" * 70)
# --- exclusion outcome on the real benchmark --------------------------
scr = screen_portfolio(b["holdings"])
print("\n[1] PAB exclusions on the real SX5E benchmark")
print("-" * 70)
for r in scr["results"]:
if r.excluded:
why = ", ".join(x["screen"] for x in r.breaches)
print(f" EXCLUDED {r.issuer:<20} ({why})")
s = scr["summary"]
print(f" {s['n_excluded']} excluded ({s['weight_excluded_pct']:.1f}% of weight); "
f"{s['weight_clear_pct']:.1f}% clear")
# --- three compliance scenarios ---------------------------------------
sc = run_scenarios(b, year=YEAR, max_weight=MAX_W)
print(f"\n[2] Compliance-path cost (carbon target = 1.5C pathway "
f"{sc['pathway_target']:.1f} tCO2e/EURm; {int(MAX_W*100)}% name cap)")
print("-" * 70)
print(f" Parent benchmark WACI: {sc['parent_waci']:.1f} tCO2e/EURm")
print(f" {'Scenario':<24}{'TE (bps)':>10}{'WACI':>8}{'Qual wt':>9}{'Feasible':>10}")
for k, r in sc["results"].items():
print(f" {r['label']:<24}{r['te_bps']:>10.1f}{r['waci']:>8.1f}"
f"{r['qualifying_weight']*100:>8.0f}%{('Yes' if r['success'] else 'No'):>10}")
# --- cost-of-green frontier -------------------------------------------
print("\n[3] Cost-of-green frontier")
print("-" * 70)
df = cost_of_green_frontier(b, year=YEAR, n_points=25, floor=None,
max_weight=MAX_W)
feas = df[df.feasible]
print(f" Parent WACI {df.attrs['parent_waci']:.1f} -> "
f"pathway target {df.attrs['pathway_target']:.1f} tCO2e/EURm")
print(f" {len(feas)} feasible points; "
f"TE {feas.tracking_error_bps.min():.0f}-{feas.tracking_error_bps.max():.0f} bps")
print(f" {'Intensity cut %':>16}{'Achieved WACI':>15}{'TE (bps)':>11}")
for _, row in feas.iloc[::3].iterrows():
print(f" {row['intensity_cut_pct']:>16.1f}{row['achieved_waci']:>15.1f}"
f"{row['tracking_error_bps']:>11.1f}")
# --- plot --------------------------------------------------------------
fig, ax = plt.subplots(figsize=(8.5, 5.2))
ax.plot(feas["intensity_cut_pct"], feas["tracking_error_bps"],
"o-", color="#1E2761", lw=2, ms=5, label="Min-TE frontier")
# mark the pathway-aligned point
parent = df.attrs["parent_waci"]
tgt = df.attrs["pathway_target"]
pathway_cut = 100.0 * (parent - tgt) / parent
ax.axvline(pathway_cut, color="#C47D00", ls="--", lw=1.5,
label=f"1.5C pathway ({pathway_cut:.0f}% cut)")
ax.set_xlabel("Carbon-intensity reduction vs parent benchmark (%)")
ax.set_ylabel("Tracking error (bps p.a.)")
ax.set_title("The cost of green: active risk vs decarbonisation\n"
"SX5E universe, minimum tracking error, 12% name cap",
fontsize=12, fontweight="bold")
ax.grid(alpha=0.25)
ax.legend()
fig.tight_layout()
fig.savefig("cost_of_green_frontier.png", dpi=130)
print("\n Saved chart: cost_of_green_frontier.png")
print("-" * 70)
if __name__ == "__main__":
main()