Spaces:
Running
Running
AJAY KASU commited on
Commit ·
aca4b95
1
Parent(s): a7bad29
Fix: Explicit Sector Truth Tables to prevent AI Hallucination
Browse files- ai/ai_reporter.py +1 -0
- ai/prompts.py +3 -0
- analytics/attribution.py +23 -0
- core/schema.py +1 -0
ai/ai_reporter.py
CHANGED
|
@@ -50,6 +50,7 @@ INSTRUCTION: Start your commentary exactly with the header: "Market Commentary -
|
|
| 50 |
selection_effect=attribution_report.selection_effect * 100,
|
| 51 |
top_contributors=json.dumps(attribution_report.top_contributors, indent=2),
|
| 52 |
top_detractors=json.dumps(attribution_report.top_detractors, indent=2),
|
|
|
|
| 53 |
current_date=current_date # Pass date to template
|
| 54 |
)
|
| 55 |
|
|
|
|
| 50 |
selection_effect=attribution_report.selection_effect * 100,
|
| 51 |
top_contributors=json.dumps(attribution_report.top_contributors, indent=2),
|
| 52 |
top_detractors=json.dumps(attribution_report.top_detractors, indent=2),
|
| 53 |
+
sector_positioning=json.dumps(attribution_report.sector_exposure, indent=2), # Truth Table Injection
|
| 54 |
current_date=current_date # Pass date to template
|
| 55 |
)
|
| 56 |
|
ai/prompts.py
CHANGED
|
@@ -37,6 +37,9 @@ Write a "Trailing 30-Day Risk & Performance Attribution" report relative to the
|
|
| 37 |
**Top Active Detractors (JSON)**:
|
| 38 |
{top_detractors}
|
| 39 |
|
|
|
|
|
|
|
|
|
|
| 40 |
## Guidelines for the Narrative:
|
| 41 |
1. **Timeframe**: Use the EXACT date provided: "{current_date}".
|
| 42 |
2. **Ticker Validation**: Use the Ticker symbols exactly as listed.
|
|
|
|
| 37 |
**Top Active Detractors (JSON)**:
|
| 38 |
{top_detractors}
|
| 39 |
|
| 40 |
+
**Sector Positioning (The "Truth Table")**:
|
| 41 |
+
{sector_positioning}
|
| 42 |
+
|
| 43 |
## Guidelines for the Narrative:
|
| 44 |
1. **Timeframe**: Use the EXACT date provided: "{current_date}".
|
| 45 |
2. **Ticker Validation**: Use the Ticker symbols exactly as listed.
|
analytics/attribution.py
CHANGED
|
@@ -120,6 +120,28 @@ class AttributionEngine:
|
|
| 120 |
top_contributors = build_truth_table(sorted_contrib, 5)
|
| 121 |
top_detractors = build_truth_table(sorted_contrib.sort_values(by='contribution', ascending=True), 5)
|
| 122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
# Narrative skeleton (to be filled by AI)
|
| 124 |
narrative_raw = (
|
| 125 |
f"Total Active Return: {(total_allocation + total_selection + total_interaction):.4f}. "
|
|
@@ -133,5 +155,6 @@ class AttributionEngine:
|
|
| 133 |
total_active_return=(total_allocation + total_selection + total_interaction),
|
| 134 |
top_contributors=top_contributors,
|
| 135 |
top_detractors=top_detractors,
|
|
|
|
| 136 |
narrative=narrative_raw
|
| 137 |
)
|
|
|
|
| 120 |
top_contributors = build_truth_table(sorted_contrib, 5)
|
| 121 |
top_detractors = build_truth_table(sorted_contrib.sort_values(by='contribution', ascending=True), 5)
|
| 122 |
|
| 123 |
+
# Build Sector Exposure Truth Table
|
| 124 |
+
sector_exposure = []
|
| 125 |
+
for sector, data in sector_groups:
|
| 126 |
+
w_p = data['wp'].sum()
|
| 127 |
+
w_b = data['wb'].sum()
|
| 128 |
+
|
| 129 |
+
if w_p == 0.0 and w_b > 0.0:
|
| 130 |
+
status = "Excluded"
|
| 131 |
+
elif w_p < w_b:
|
| 132 |
+
status = "Underweight"
|
| 133 |
+
elif w_p > w_b:
|
| 134 |
+
status = "Overweight"
|
| 135 |
+
else:
|
| 136 |
+
status = "Neutral"
|
| 137 |
+
|
| 138 |
+
sector_exposure.append({
|
| 139 |
+
"Sector": sector,
|
| 140 |
+
"Portfolio_Weight": f"{w_p:.2%}",
|
| 141 |
+
"Benchmark_Weight": f"{w_b:.2%}",
|
| 142 |
+
"Status": status
|
| 143 |
+
})
|
| 144 |
+
|
| 145 |
# Narrative skeleton (to be filled by AI)
|
| 146 |
narrative_raw = (
|
| 147 |
f"Total Active Return: {(total_allocation + total_selection + total_interaction):.4f}. "
|
|
|
|
| 155 |
total_active_return=(total_allocation + total_selection + total_interaction),
|
| 156 |
top_contributors=top_contributors,
|
| 157 |
top_detractors=top_detractors,
|
| 158 |
+
sector_exposure=sector_exposure,
|
| 159 |
narrative=narrative_raw
|
| 160 |
)
|
core/schema.py
CHANGED
|
@@ -97,4 +97,5 @@ class AttributionReport(BaseModel):
|
|
| 97 |
total_active_return: float
|
| 98 |
top_contributors: List[Dict]
|
| 99 |
top_detractors: List[Dict]
|
|
|
|
| 100 |
narrative: str
|
|
|
|
| 97 |
total_active_return: float
|
| 98 |
top_contributors: List[Dict]
|
| 99 |
top_detractors: List[Dict]
|
| 100 |
+
sector_exposure: Optional[List[Dict]] = Field(default_factory=list, description="Sector Level Attribution (Truth Table)")
|
| 101 |
narrative: str
|