diff --git a/CertReports.Syncfusion/Controllers/FundChartController.cs b/CertReports.Syncfusion/Controllers/FundChartController.cs
new file mode 100644
index 0000000..a4fc268
--- /dev/null
+++ b/CertReports.Syncfusion/Controllers/FundChartController.cs
@@ -0,0 +1,139 @@
+using CertReports.Syncfusion.Services.Implementations;
+using CertReports.Syncfusion.Services.Interfaces;
+using Microsoft.AspNetCore.Mvc;
+using Syncfusion.Pdf;
+using Syncfusion.Pdf.Graphics;
+
+namespace CertReports.Syncfusion.Controllers;
+
+///
+/// Endpoint:
+/// GET /api/chart/fund/{isin}[?format=png|jpg|jpeg|pdf&width=&height=&save=true]
+///
+[ApiController]
+[Route("api/chart/fund")]
+public class FundChartController : ControllerBase
+{
+ private readonly IFundDataService _dataService;
+ private readonly IConfiguration _configuration;
+ private readonly ILogger _logger;
+
+ public FundChartController(
+ IFundDataService dataService,
+ IConfiguration configuration,
+ ILogger logger)
+ {
+ _dataService = dataService;
+ _configuration = configuration;
+ _logger = logger;
+ }
+
+ [HttpGet("{isin}")]
+ public async Task GetChart(
+ string isin,
+ [FromQuery] string format = "png",
+ [FromQuery] int width = 1100,
+ [FromQuery] int height = 700,
+ [FromQuery] bool save = false)
+ {
+ if (string.IsNullOrWhiteSpace(isin))
+ return BadRequest("ISIN richiesto.");
+
+ width = Math.Clamp(width, 400, 2000);
+ height = Math.Clamp(height, 300, 1500);
+
+ try
+ {
+ var info = await _dataService.GetFundInfoAsync(isin);
+ var points = await _dataService.GetChartPricesAsync(isin);
+
+ if (info == null || points.Count == 0)
+ return NotFound($"Nessun dato per ISIN {isin}");
+
+ bool isJpeg = format.Equals("jpg", StringComparison.OrdinalIgnoreCase)
+ || format.Equals("jpeg", StringComparison.OrdinalIgnoreCase);
+
+ if (format.Equals("pdf", StringComparison.OrdinalIgnoreCase))
+ {
+ var pngBytes = FundSkiaChartRenderer.Render(points, info.Strumento, width, height);
+ var pdfBytes = WrapPngInPdf(pngBytes);
+ Response.Headers.Append("Content-Disposition",
+ $"inline; filename=chart_fund_{isin}.pdf");
+ return File(pdfBytes, "application/pdf");
+ }
+
+ var imgBytes = FundSkiaChartRenderer.Render(points, info.Strumento, width, height, jpeg: isJpeg);
+
+ if (save && isJpeg)
+ await SaveChartToDiskAsync(isin, imgBytes);
+
+ if (isJpeg)
+ {
+ Response.Headers.Append("Content-Disposition",
+ $"inline; filename=chart_fund_{isin}.jpg");
+ return File(imgBytes, "image/jpeg");
+ }
+
+ Response.Headers.Append("Content-Disposition",
+ $"inline; filename=chart_fund_{isin}.png");
+ return File(imgBytes, "image/png");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Errore generazione chart fondo per ISIN {Isin}", isin);
+ return StatusCode(500, new { status = "KO", message = "Errore nella generazione del grafico." });
+ }
+ }
+
+ private async Task SaveChartToDiskAsync(string isin, byte[] imgBytes)
+ {
+ try
+ {
+ string? folder = _configuration["ChartSettings:SavePath"];
+ if (string.IsNullOrEmpty(folder))
+ {
+ _logger.LogWarning("Salvataggio chart fondo saltato: ChartSettings:SavePath non configurato");
+ return;
+ }
+
+ Directory.CreateDirectory(folder);
+ var fullPath = Path.Combine(folder, $"{isin}_fund.jpg");
+ await System.IO.File.WriteAllBytesAsync(fullPath, imgBytes);
+ _logger.LogInformation("Chart fondo salvato in {Path}", fullPath);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "Salvataggio chart fondo fallito per {Isin}", isin);
+ }
+ }
+
+ private static byte[] WrapPngInPdf(byte[] pngBytes)
+ {
+ var doc = new PdfDocument();
+ doc.PageSettings.Size = PdfPageSize.A4;
+ doc.PageSettings.Orientation = PdfPageOrientation.Landscape;
+ doc.PageSettings.Margins.All = 30;
+
+ var page = doc.Pages.Add();
+ var g = page.Graphics;
+ float pw = page.GetClientSize().Width;
+ float ph = page.GetClientSize().Height;
+
+ using var stream = new MemoryStream(pngBytes);
+ var img = new PdfBitmap(stream);
+
+ float ratio = (float)img.Width / img.Height;
+ float dw = pw;
+ float dh = dw / ratio;
+ if (dh > ph) { dh = ph; dw = dh * ratio; }
+
+ float x = (pw - dw) / 2;
+ float y = (ph - dh) / 2;
+ g.DrawImage(img, x, y, dw, dh);
+
+ using var output = new MemoryStream();
+ doc.Save(output);
+ doc.Close(true);
+ return output.ToArray();
+ }
+}