Spaces:
Sleeping
Sleeping
| 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() | |