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 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