diff --git a/cli/portfolio_pdf_generator.py b/cli/portfolio_pdf_generator.py
index 776bbc18..4f2aab88 100644
--- a/cli/portfolio_pdf_generator.py
+++ b/cli/portfolio_pdf_generator.py
@@ -1,6 +1,7 @@
"""PDF report generator for portfolio analysis results."""
import datetime
import io
+import re
from pathlib import Path
from typing import Dict, Any
@@ -291,6 +292,25 @@ def generate_portfolio_pdf_report(
elements.append(t)
elements.append(Spacer(1, 30))
+ # Helper function to clean text for PDF
+ def clean_text(text: str) -> str:
+ """Clean text and properly escape for reportlab."""
+ # Escape special XML/HTML characters first
+ text = text.replace('&', '&')
+ text = text.replace('<', '<')
+ text = text.replace('>', '>')
+
+ # Convert markdown bold (**text**) to HTML bold
+ text = re.sub(r'\*\*(.+?)\*\*', r'\1', text)
+
+ # Convert markdown italic (*text*) to HTML italic
+ text = re.sub(r'(?\1', text)
+
+ # Remove markdown headers
+ text = re.sub(r'^#{1,6}\s+', '', text, flags=re.MULTILINE)
+
+ return text
+
# Generate charts
print("Generating portfolio charts...")
charts = generate_portfolio_charts(result)
@@ -329,7 +349,12 @@ def generate_portfolio_pdf_report(
elements.append(Spacer(1, 12))
for line in result.portfolio_recommendation.split('\n'):
if line.strip():
- elements.append(Paragraph(line.replace('#', '').replace('**', '').replace('**', ''), styles['CustomBody']))
+ cleaned_line = clean_text(line)
+ try:
+ elements.append(Paragraph(cleaned_line, styles['CustomBody']))
+ except Exception as e:
+ print(f"Warning: Could not parse line, adding as plain text: {e}")
+ elements.append(Paragraph(cleaned_line.replace('<', '<').replace('>', '>'), styles['CustomBody']))
# Risk Assessment
elements.append(PageBreak())
@@ -338,7 +363,12 @@ def generate_portfolio_pdf_report(
elements.append(Spacer(1, 12))
for line in result.risk_assessment.split('\n'):
if line.strip():
- elements.append(Paragraph(line.replace('#', '').replace('**', '').replace('**', ''), styles['CustomBody']))
+ cleaned_line = clean_text(line)
+ try:
+ elements.append(Paragraph(cleaned_line, styles['CustomBody']))
+ except Exception as e:
+ print(f"Warning: Could not parse line, adding as plain text: {e}")
+ elements.append(Paragraph(cleaned_line.replace('<', '<').replace('>', '>'), styles['CustomBody']))
# Rebalancing Suggestions
if result.rebalancing_suggestions:
diff --git a/test_portfolio_analysis.py b/test_portfolio_analysis.py
old mode 100644
new mode 100755
index 56cd0d3b..008fbb12
--- a/test_portfolio_analysis.py
+++ b/test_portfolio_analysis.py
@@ -1,5 +1,11 @@
+#!/usr/bin/env python3
"""Test portfolio analysis functionality."""
from pathlib import Path
+from dotenv import load_dotenv
+
+# Load environment variables
+load_dotenv()
+
from tradingagents.portfolio.models import Portfolio, Position
from tradingagents.portfolio.portfolio_graph import PortfolioAnalysisGraph
from tradingagents.default_config import DEFAULT_CONFIG