Spaces:
Running
Running
File size: 3,525 Bytes
c3aab0c 247775e c3aab0c 247775e c3aab0c 247775e c3aab0c 247775e c3aab0c 247775e c3aab0c | 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 |
import pandas as pd
from analytics.attribution import AttributionEngine
def test_attribution_logic():
print("Testing Attribution Logic...")
# Mock Data
# Scenario:
# - AAPL: Overweight (Held 5%, Bench 4%). Return +10%. Should be Contributor.
# - MSFT: Excluded (Held 0%, Bench 6%). Return +10%. Should be Detractor (Missed Rally).
# - GOOG: Neutral (Held 2%, Bench 2%). Return -5%. Active Contrib 0.
portfolio_weights = {"AAPL": 0.05, "MSFT": 0.0, "GOOG": 0.02}
benchmark_weights = {"AAPL": 0.04, "MSFT": 0.06, "GOOG": 0.02}
# Returns for the period
returns_data = {"AAPL": 0.10, "MSFT": 0.10, "GOOG": -0.05}
asset_returns = pd.Series(returns_data)
sector_map = {
"AAPL": "Technology",
"MSFT": "Technology",
"GOOG": "Communication Services"
}
engine = AttributionEngine()
report = engine.generate_attribution_report(
portfolio_weights,
benchmark_weights,
asset_returns,
sector_map
)
print("\n--- Attribution Report Generated ---")
print(f"Total Active Return: {report.total_active_return:.4f}")
print("\n[Top Contributors]")
for item in report.top_contributors:
print(item)
print("\n[Top Detractors]")
for item in report.top_detractors:
print(item)
# Validation Logic
# MSFT Active Weight = 0 - 0.06 = -0.06
# MSFT Active Contrib = -0.06 * 0.10 = -0.006 (Detractor)
msft = next((x for x in report.top_detractors if x['Ticker'] == 'MSFT'), None)
if msft:
# Signage Logic Verification
# MSFT is Excluded and Return is +10%. This is BAD. Should be 'Drag' or 'Missed Rally'
print(f"MSFT Reasoning: {msft.get('Reasoning', 'N/A')}")
if "Drag" in msft.get('Reasoning', '') or "Missed" in msft.get('Reasoning', ''):
print("SUCCESS: MSFT correctly identified as Missed Rally (Drag).")
else:
print(f"FAILURE: MSFT reasoning wrong: {msft}")
if msft['Status'] == "Excluded" and float(msft['Active_Contribution']) < 0:
print("SUCCESS: MSFT correctly identified as Excluded Detractor.")
else:
print(f"FAILURE: MSFT status/logic wrong: {msft}")
else:
print("FAILURE: MSFT not found in detractors.")
# AAPL Active Weight = 0.05 - 0.04 = +0.01
# AAPL Active Contrib = +0.01 * 0.10 = +0.001 (Contributor)
aapl = next((x for x in report.top_contributors if x['Ticker'] == 'AAPL'), None)
current_return = float(aapl['Active_Contribution']) if aapl else 0
if aapl and current_return > 0:
print("SUCCESS: AAPL correctly identified as Overweight Contributor.")
else:
print(f"FAILURE: AAPL logic wrong. {aapl}")
# VERIFICATION: Sector Exposure Truth Table
print("\n[Sector Exposure Truth Table]")
tech_exposure = next((x for x in report.sector_exposure if x['Sector'] == 'Technology'), None)
if tech_exposure:
print(f"Technology Exposure: {tech_exposure}")
# We hold AAPL (5%) vs Bench AAPL+MSFT (10%) -> Underweight
if tech_exposure['Status'] == "Underweight":
print("SUCCESS: Technology correctly identified as UNDERWEIGHT (not Excluded).")
else:
print(f"FAILURE: Technology status wrong: {tech_exposure['Status']}")
else:
print("FAILURE: Technology sector missing from report.")
if __name__ == "__main__":
test_attribution_logic()
|