Spaces:
Sleeping
Sleeping
feat: SWOT output as structured text parsed into HTML tables
Browse files- Backend: Updated analyzer prompt OUTPUT FORMAT to use structured text
format: [M##] Metric: Value - Insight
- Backend: Updated revision prompt to match the same format
- Frontend: Added parseSwotLine() parser to extract ref, metric, insight
- Frontend: Replaced SWOT list display with HTML tables (3 columns)
- Frontend: Removed unused icon imports (Zap, AlertCircle)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- frontend/src/App.tsx +139 -85
- src/nodes/analyzer.py +29 -32
frontend/src/App.tsx
CHANGED
|
@@ -27,10 +27,8 @@ import {
|
|
| 27 |
AlertTriangle,
|
| 28 |
CheckCircle,
|
| 29 |
XCircle,
|
| 30 |
-
AlertCircle,
|
| 31 |
BarChart3,
|
| 32 |
RefreshCw,
|
| 33 |
-
Zap,
|
| 34 |
Play,
|
| 35 |
Copy,
|
| 36 |
Download,
|
|
@@ -94,6 +92,51 @@ const cleanMarkdown = (text: string): string => {
|
|
| 94 |
.trim()
|
| 95 |
}
|
| 96 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
const Index = () => {
|
| 98 |
const [selectedStock, setSelectedStock] = useState<StockResult | null>(null)
|
| 99 |
const [isLoading, setIsLoading] = useState(false)
|
|
@@ -781,119 +824,130 @@ Generated by Instant SWOT Agent`
|
|
| 781 |
/>
|
| 782 |
)}
|
| 783 |
|
| 784 |
-
{/* SWOT Analysis */}
|
| 785 |
<div className="space-y-6">
|
| 786 |
-
{/* Strengths */}
|
| 787 |
<div>
|
| 788 |
<h3 className="flex items-center gap-2 text-base font-semibold text-emerald-500 mb-3 border-b border-emerald-500/30 pb-2">
|
| 789 |
<TrendingUp className="h-5 w-5" />
|
| 790 |
Strengths
|
| 791 |
</h3>
|
| 792 |
-
<
|
| 793 |
-
|
| 794 |
-
<
|
| 795 |
-
<
|
| 796 |
-
<
|
| 797 |
-
|
| 798 |
-
|
| 799 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 800 |
</div>
|
| 801 |
|
| 802 |
-
{/* Weaknesses */}
|
| 803 |
<div>
|
| 804 |
<h3 className="flex items-center gap-2 text-base font-semibold text-red-500 mb-3 border-b border-red-500/30 pb-2">
|
| 805 |
<TrendingDown className="h-5 w-5" />
|
| 806 |
Weaknesses
|
| 807 |
</h3>
|
| 808 |
-
<
|
| 809 |
-
|
| 810 |
-
<
|
| 811 |
-
<
|
| 812 |
-
<
|
| 813 |
-
|
| 814 |
-
|
| 815 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 816 |
</div>
|
| 817 |
|
| 818 |
-
{/* Opportunities */}
|
| 819 |
<div>
|
| 820 |
<h3 className="flex items-center gap-2 text-base font-semibold text-blue-500 mb-3 border-b border-blue-500/30 pb-2">
|
| 821 |
<Target className="h-5 w-5" />
|
| 822 |
Opportunities
|
| 823 |
</h3>
|
| 824 |
-
<
|
| 825 |
-
|
| 826 |
-
<
|
| 827 |
-
<
|
| 828 |
-
<
|
| 829 |
-
|
| 830 |
-
|
| 831 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 832 |
</div>
|
| 833 |
|
| 834 |
-
{/* Threats */}
|
| 835 |
<div>
|
| 836 |
<h3 className="flex items-center gap-2 text-base font-semibold text-yellow-500 mb-3 border-b border-yellow-500/30 pb-2">
|
| 837 |
<AlertTriangle className="h-5 w-5" />
|
| 838 |
Threats
|
| 839 |
</h3>
|
| 840 |
-
<
|
| 841 |
-
|
| 842 |
-
<
|
| 843 |
-
<
|
| 844 |
-
<
|
| 845 |
-
|
| 846 |
-
|
| 847 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 848 |
</div>
|
| 849 |
</div>
|
| 850 |
|
| 851 |
-
{/* Data Quality Notes */}
|
| 852 |
-
<Card>
|
| 853 |
-
<CardHeader>
|
| 854 |
-
<CardTitle className="text-base flex items-center gap-2">
|
| 855 |
-
<Target className="h-4 w-4" />
|
| 856 |
-
Data Quality Notes
|
| 857 |
-
</CardTitle>
|
| 858 |
-
</CardHeader>
|
| 859 |
-
<CardContent className="space-y-4">
|
| 860 |
-
{analysisResult.quality_notes?.high_confidence?.length > 0 && (
|
| 861 |
-
<div>
|
| 862 |
-
<h4 className="font-medium text-sm mb-2">Metrics with High Confidence</h4>
|
| 863 |
-
<ul className="text-sm text-muted-foreground space-y-1">
|
| 864 |
-
{analysisResult.quality_notes.high_confidence.map((m: string, i: number) => (
|
| 865 |
-
<li key={i}>- {m}</li>
|
| 866 |
-
))}
|
| 867 |
-
</ul>
|
| 868 |
-
</div>
|
| 869 |
-
)}
|
| 870 |
-
{analysisResult.quality_notes?.gaps_or_stale?.length > 0 && (
|
| 871 |
-
<div>
|
| 872 |
-
<h4 className="font-medium text-sm mb-2">Data Gaps or Staleness</h4>
|
| 873 |
-
<ul className="text-sm text-muted-foreground space-y-1">
|
| 874 |
-
{analysisResult.quality_notes.gaps_or_stale.map((m: string, i: number) => (
|
| 875 |
-
<li key={i}>- {m}</li>
|
| 876 |
-
))}
|
| 877 |
-
</ul>
|
| 878 |
-
</div>
|
| 879 |
-
)}
|
| 880 |
-
{analysisResult.quality_notes?.assumptions?.length > 0 && (
|
| 881 |
-
<div>
|
| 882 |
-
<h4 className="font-medium text-sm mb-2">Assumptions Made</h4>
|
| 883 |
-
<ul className="text-sm text-muted-foreground space-y-1">
|
| 884 |
-
{analysisResult.quality_notes.assumptions.map((a: string, i: number) => (
|
| 885 |
-
<li key={i}>- {a}</li>
|
| 886 |
-
))}
|
| 887 |
-
</ul>
|
| 888 |
-
</div>
|
| 889 |
-
)}
|
| 890 |
-
{(!analysisResult.quality_notes?.high_confidence?.length &&
|
| 891 |
-
!analysisResult.quality_notes?.gaps_or_stale?.length &&
|
| 892 |
-
!analysisResult.quality_notes?.assumptions?.length) && (
|
| 893 |
-
<p className="text-sm text-muted-foreground">No quality notes available.</p>
|
| 894 |
-
)}
|
| 895 |
-
</CardContent>
|
| 896 |
-
</Card>
|
| 897 |
</div>
|
| 898 |
)}
|
| 899 |
</TabsContent>
|
|
|
|
| 27 |
AlertTriangle,
|
| 28 |
CheckCircle,
|
| 29 |
XCircle,
|
|
|
|
| 30 |
BarChart3,
|
| 31 |
RefreshCw,
|
|
|
|
| 32 |
Play,
|
| 33 |
Copy,
|
| 34 |
Download,
|
|
|
|
| 92 |
.trim()
|
| 93 |
}
|
| 94 |
|
| 95 |
+
// Parse structured SWOT line: "[M01] Revenue: $394.3B - Strong market position"
|
| 96 |
+
// Returns { ref, metric, insight } or null if line doesn't match pattern
|
| 97 |
+
interface SwotRow {
|
| 98 |
+
ref: string
|
| 99 |
+
metric: string
|
| 100 |
+
insight: string
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
const parseSwotLine = (line: string): SwotRow | null => {
|
| 104 |
+
// Clean markdown first
|
| 105 |
+
const cleaned = cleanMarkdown(line)
|
| 106 |
+
|
| 107 |
+
// Pattern: [M##] Metric: Value - Insight
|
| 108 |
+
// Also handle: [M##] Metric: Value | Insight (pipe separator)
|
| 109 |
+
const match = cleaned.match(/^\[?(M\d+)\]?\s*(.+?)\s*[-|]\s*(.+)$/i)
|
| 110 |
+
if (match) {
|
| 111 |
+
return {
|
| 112 |
+
ref: match[1],
|
| 113 |
+
metric: match[2].trim(),
|
| 114 |
+
insight: match[3].trim()
|
| 115 |
+
}
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
// Fallback: no ref pattern, just "Metric: Value - Insight"
|
| 119 |
+
const fallback = cleaned.match(/^(.+?)\s*[-|]\s*(.+)$/)
|
| 120 |
+
if (fallback) {
|
| 121 |
+
return {
|
| 122 |
+
ref: '',
|
| 123 |
+
metric: fallback[1].trim(),
|
| 124 |
+
insight: fallback[2].trim()
|
| 125 |
+
}
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
// Last resort: just return the cleaned line as insight
|
| 129 |
+
if (cleaned.length > 0) {
|
| 130 |
+
return {
|
| 131 |
+
ref: '',
|
| 132 |
+
metric: '',
|
| 133 |
+
insight: cleaned
|
| 134 |
+
}
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
return null
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
const Index = () => {
|
| 141 |
const [selectedStock, setSelectedStock] = useState<StockResult | null>(null)
|
| 142 |
const [isLoading, setIsLoading] = useState(false)
|
|
|
|
| 824 |
/>
|
| 825 |
)}
|
| 826 |
|
| 827 |
+
{/* SWOT Analysis - Tables */}
|
| 828 |
<div className="space-y-6">
|
| 829 |
+
{/* Strengths Table */}
|
| 830 |
<div>
|
| 831 |
<h3 className="flex items-center gap-2 text-base font-semibold text-emerald-500 mb-3 border-b border-emerald-500/30 pb-2">
|
| 832 |
<TrendingUp className="h-5 w-5" />
|
| 833 |
Strengths
|
| 834 |
</h3>
|
| 835 |
+
<table className="w-full text-sm border-collapse">
|
| 836 |
+
<thead>
|
| 837 |
+
<tr className="border-b border-border">
|
| 838 |
+
<th className="text-left py-2 px-2 w-12 text-muted-foreground font-medium">Ref</th>
|
| 839 |
+
<th className="text-left py-2 px-2 w-1/3 text-muted-foreground font-medium">Metric</th>
|
| 840 |
+
<th className="text-left py-2 px-2 text-muted-foreground font-medium">Insight</th>
|
| 841 |
+
</tr>
|
| 842 |
+
</thead>
|
| 843 |
+
<tbody>
|
| 844 |
+
{analysisResult.swot_data.strengths.map((item, i) => {
|
| 845 |
+
const parsed = parseSwotLine(item)
|
| 846 |
+
if (!parsed) return null
|
| 847 |
+
return (
|
| 848 |
+
<tr key={i} className="border-b border-border/50 hover:bg-emerald-500/5">
|
| 849 |
+
<td className="py-2 px-2 text-emerald-500 font-mono text-xs">{parsed.ref}</td>
|
| 850 |
+
<td className="py-2 px-2 text-foreground">{parsed.metric}</td>
|
| 851 |
+
<td className="py-2 px-2 text-muted-foreground">{parsed.insight}</td>
|
| 852 |
+
</tr>
|
| 853 |
+
)
|
| 854 |
+
})}
|
| 855 |
+
</tbody>
|
| 856 |
+
</table>
|
| 857 |
</div>
|
| 858 |
|
| 859 |
+
{/* Weaknesses Table */}
|
| 860 |
<div>
|
| 861 |
<h3 className="flex items-center gap-2 text-base font-semibold text-red-500 mb-3 border-b border-red-500/30 pb-2">
|
| 862 |
<TrendingDown className="h-5 w-5" />
|
| 863 |
Weaknesses
|
| 864 |
</h3>
|
| 865 |
+
<table className="w-full text-sm border-collapse">
|
| 866 |
+
<thead>
|
| 867 |
+
<tr className="border-b border-border">
|
| 868 |
+
<th className="text-left py-2 px-2 w-12 text-muted-foreground font-medium">Ref</th>
|
| 869 |
+
<th className="text-left py-2 px-2 w-1/3 text-muted-foreground font-medium">Metric</th>
|
| 870 |
+
<th className="text-left py-2 px-2 text-muted-foreground font-medium">Insight</th>
|
| 871 |
+
</tr>
|
| 872 |
+
</thead>
|
| 873 |
+
<tbody>
|
| 874 |
+
{analysisResult.swot_data.weaknesses.map((item, i) => {
|
| 875 |
+
const parsed = parseSwotLine(item)
|
| 876 |
+
if (!parsed) return null
|
| 877 |
+
return (
|
| 878 |
+
<tr key={i} className="border-b border-border/50 hover:bg-red-500/5">
|
| 879 |
+
<td className="py-2 px-2 text-red-500 font-mono text-xs">{parsed.ref}</td>
|
| 880 |
+
<td className="py-2 px-2 text-foreground">{parsed.metric}</td>
|
| 881 |
+
<td className="py-2 px-2 text-muted-foreground">{parsed.insight}</td>
|
| 882 |
+
</tr>
|
| 883 |
+
)
|
| 884 |
+
})}
|
| 885 |
+
</tbody>
|
| 886 |
+
</table>
|
| 887 |
</div>
|
| 888 |
|
| 889 |
+
{/* Opportunities Table */}
|
| 890 |
<div>
|
| 891 |
<h3 className="flex items-center gap-2 text-base font-semibold text-blue-500 mb-3 border-b border-blue-500/30 pb-2">
|
| 892 |
<Target className="h-5 w-5" />
|
| 893 |
Opportunities
|
| 894 |
</h3>
|
| 895 |
+
<table className="w-full text-sm border-collapse">
|
| 896 |
+
<thead>
|
| 897 |
+
<tr className="border-b border-border">
|
| 898 |
+
<th className="text-left py-2 px-2 w-12 text-muted-foreground font-medium">Ref</th>
|
| 899 |
+
<th className="text-left py-2 px-2 w-1/3 text-muted-foreground font-medium">Metric</th>
|
| 900 |
+
<th className="text-left py-2 px-2 text-muted-foreground font-medium">Insight</th>
|
| 901 |
+
</tr>
|
| 902 |
+
</thead>
|
| 903 |
+
<tbody>
|
| 904 |
+
{analysisResult.swot_data.opportunities.map((item, i) => {
|
| 905 |
+
const parsed = parseSwotLine(item)
|
| 906 |
+
if (!parsed) return null
|
| 907 |
+
return (
|
| 908 |
+
<tr key={i} className="border-b border-border/50 hover:bg-blue-500/5">
|
| 909 |
+
<td className="py-2 px-2 text-blue-500 font-mono text-xs">{parsed.ref}</td>
|
| 910 |
+
<td className="py-2 px-2 text-foreground">{parsed.metric}</td>
|
| 911 |
+
<td className="py-2 px-2 text-muted-foreground">{parsed.insight}</td>
|
| 912 |
+
</tr>
|
| 913 |
+
)
|
| 914 |
+
})}
|
| 915 |
+
</tbody>
|
| 916 |
+
</table>
|
| 917 |
</div>
|
| 918 |
|
| 919 |
+
{/* Threats Table */}
|
| 920 |
<div>
|
| 921 |
<h3 className="flex items-center gap-2 text-base font-semibold text-yellow-500 mb-3 border-b border-yellow-500/30 pb-2">
|
| 922 |
<AlertTriangle className="h-5 w-5" />
|
| 923 |
Threats
|
| 924 |
</h3>
|
| 925 |
+
<table className="w-full text-sm border-collapse">
|
| 926 |
+
<thead>
|
| 927 |
+
<tr className="border-b border-border">
|
| 928 |
+
<th className="text-left py-2 px-2 w-12 text-muted-foreground font-medium">Ref</th>
|
| 929 |
+
<th className="text-left py-2 px-2 w-1/3 text-muted-foreground font-medium">Metric</th>
|
| 930 |
+
<th className="text-left py-2 px-2 text-muted-foreground font-medium">Insight</th>
|
| 931 |
+
</tr>
|
| 932 |
+
</thead>
|
| 933 |
+
<tbody>
|
| 934 |
+
{analysisResult.swot_data.threats.map((item, i) => {
|
| 935 |
+
const parsed = parseSwotLine(item)
|
| 936 |
+
if (!parsed) return null
|
| 937 |
+
return (
|
| 938 |
+
<tr key={i} className="border-b border-border/50 hover:bg-yellow-500/5">
|
| 939 |
+
<td className="py-2 px-2 text-yellow-500 font-mono text-xs">{parsed.ref}</td>
|
| 940 |
+
<td className="py-2 px-2 text-foreground">{parsed.metric}</td>
|
| 941 |
+
<td className="py-2 px-2 text-muted-foreground">{parsed.insight}</td>
|
| 942 |
+
</tr>
|
| 943 |
+
)
|
| 944 |
+
})}
|
| 945 |
+
</tbody>
|
| 946 |
+
</table>
|
| 947 |
</div>
|
| 948 |
</div>
|
| 949 |
|
| 950 |
+
{/* Data Quality Notes - Hidden for now, will revisit later */}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 951 |
</div>
|
| 952 |
)}
|
| 953 |
</TabsContent>
|
src/nodes/analyzer.py
CHANGED
|
@@ -1348,17 +1348,29 @@ Weighted Score: {critique_details.get('weighted_score', 0):.1f} / 10
|
|
| 1348 |
|
| 1349 |
### OUTPUT INSTRUCTIONS
|
| 1350 |
|
| 1351 |
-
Produce a complete, revised SWOT analysis
|
| 1352 |
|
| 1353 |
## Strengths
|
| 1354 |
-
|
| 1355 |
-
|
| 1356 |
-
| M## | Metric: Value | Strategic insight in one sentence |
|
| 1357 |
|
| 1358 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1359 |
|
| 1360 |
Do not:
|
| 1361 |
-
- Use bullet points or **bold** labels - use tables only
|
| 1362 |
- Include any preamble about revisions
|
| 1363 |
- Reference the Critic's feedback in your output
|
| 1364 |
|
|
@@ -1404,42 +1416,27 @@ def _build_analyzer_prompt(company: str, ticker: str, formatted_data: str,
|
|
| 1404 |
|
| 1405 |
=== OUTPUT FORMAT ===
|
| 1406 |
|
| 1407 |
-
Produce a SWOT analysis
|
| 1408 |
|
| 1409 |
## Strengths
|
| 1410 |
-
|
| 1411 |
-
|
| 1412 |
-
| M01 | Revenue: $394.3B | Strong market position with substantial scale |
|
| 1413 |
-
| M02 | Net Margin: 24.3% | High profitability indicates pricing power |
|
| 1414 |
-
|
| 1415 |
-
(Include 3-5 rows per section)
|
| 1416 |
|
| 1417 |
## Weaknesses
|
| 1418 |
-
|
| 1419 |
-
|-----|--------|---------|
|
| 1420 |
-
| M04 | Debt/Equity: 1.87 | Elevated leverage increases financial risk |
|
| 1421 |
|
| 1422 |
## Opportunities
|
| 1423 |
-
|
| 1424 |
-
|-----|--------|---------|
|
| 1425 |
-
| M12 | GDP Growth: 4.3% | Favorable macro environment for expansion |
|
| 1426 |
|
| 1427 |
## Threats
|
| 1428 |
-
|
| 1429 |
-
|-----|--------|---------|
|
| 1430 |
-
| M13 | Interest Rate: 3.72% | Higher borrowing costs may impact margins |
|
| 1431 |
-
|
| 1432 |
-
## Data Quality Notes
|
| 1433 |
-
- Metrics Used: [List key metrics analyzed]
|
| 1434 |
-
- Data Gaps: [Any unavailable metrics]
|
| 1435 |
-
- Confidence: [High/Medium/Low]
|
| 1436 |
|
| 1437 |
CRITICAL REQUIREMENTS:
|
| 1438 |
-
1.
|
| 1439 |
-
2.
|
| 1440 |
-
3.
|
| 1441 |
-
4.
|
| 1442 |
-
5.
|
| 1443 |
|
| 1444 |
return prompt, metric_lookup, ref_hash
|
| 1445 |
|
|
|
|
| 1348 |
|
| 1349 |
### OUTPUT INSTRUCTIONS
|
| 1350 |
|
| 1351 |
+
Produce a complete, revised SWOT analysis with this exact structure (3-5 points per section):
|
| 1352 |
|
| 1353 |
## Strengths
|
| 1354 |
+
- [M01] Revenue: $394.3B - Strong market position with substantial scale
|
| 1355 |
+
- [M02] Net Margin: 24.3% - High profitability indicates pricing power
|
|
|
|
| 1356 |
|
| 1357 |
+
## Weaknesses
|
| 1358 |
+
- [M04] Debt/Equity: 1.87 - Elevated leverage increases financial risk
|
| 1359 |
+
|
| 1360 |
+
## Opportunities
|
| 1361 |
+
- [M12] GDP Growth: 4.3% - Favorable macro environment for expansion
|
| 1362 |
+
|
| 1363 |
+
## Threats
|
| 1364 |
+
- [M13] Interest Rate: 3.72% - Higher borrowing costs may impact margins
|
| 1365 |
+
|
| 1366 |
+
CRITICAL REQUIREMENTS:
|
| 1367 |
+
1. Each point MUST start with metric reference in brackets: [M##]
|
| 1368 |
+
2. Format: [M##] Metric: Value - Strategic insight
|
| 1369 |
+
3. Use EXACT values from the METRIC REFERENCE TABLE - do NOT round
|
| 1370 |
+
4. Keep insights concise (one sentence)
|
| 1371 |
+
5. Include 3-5 points per section
|
| 1372 |
|
| 1373 |
Do not:
|
|
|
|
| 1374 |
- Include any preamble about revisions
|
| 1375 |
- Reference the Critic's feedback in your output
|
| 1376 |
|
|
|
|
| 1416 |
|
| 1417 |
=== OUTPUT FORMAT ===
|
| 1418 |
|
| 1419 |
+
Produce a SWOT analysis with this exact structure (3-5 points per section):
|
| 1420 |
|
| 1421 |
## Strengths
|
| 1422 |
+
- [M01] Revenue: $394.3B - Strong market position with substantial scale
|
| 1423 |
+
- [M02] Net Margin: 24.3% - High profitability indicates pricing power
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1424 |
|
| 1425 |
## Weaknesses
|
| 1426 |
+
- [M04] Debt/Equity: 1.87 - Elevated leverage increases financial risk
|
|
|
|
|
|
|
| 1427 |
|
| 1428 |
## Opportunities
|
| 1429 |
+
- [M12] GDP Growth: 4.3% - Favorable macro environment for expansion
|
|
|
|
|
|
|
| 1430 |
|
| 1431 |
## Threats
|
| 1432 |
+
- [M13] Interest Rate: 3.72% - Higher borrowing costs may impact margins
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1433 |
|
| 1434 |
CRITICAL REQUIREMENTS:
|
| 1435 |
+
1. Each point MUST start with metric reference in brackets: [M##]
|
| 1436 |
+
2. Format: [M##] Metric: Value - Strategic insight
|
| 1437 |
+
3. Use EXACT values from the METRIC REFERENCE TABLE - do NOT round
|
| 1438 |
+
4. Keep insights concise (one sentence)
|
| 1439 |
+
5. Include 3-5 points per section"""
|
| 1440 |
|
| 1441 |
return prompt, metric_lookup, ref_hash
|
| 1442 |
|