feat: add ?years= param to underlying-only chart endpoint
Endpoint standalone /api/chart/underlying/{id} caricava un TOP 350
fisso; ora accetta ?years= (default 5, clamp 1-20) e la SP filtra per
data invece che per numero di righe.
Il prezzo usato resta Px_close/Px_closeadj in base ad
AdjustedPrices, senza fallback incrociato tra i due campi: quando
Px_closeadj non è backfillato prima di uno split (es. Apple, valorizzato
solo da inizio 2019), un fallback avrebbe mischiato dati adjusted/raw
creando un gradino artificiale nel grafico. La SP ora taglia lo storico
dove il campo scelto non è popolato.
Fix collaterale: skip righe con prezzo NULL prima del cast a decimal,
evitava un InvalidCastException con storici lunghi.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -184,23 +184,26 @@ public class ChartController : ControllerBase
|
|||||||
/// Richiede SP cedlab_Chart_UnderlyingOnly nel DB.
|
/// Richiede SP cedlab_Chart_UnderlyingOnly nel DB.
|
||||||
/// Formati supportati: png (default), jpg/jpeg, pdf. Non supporta jpgEnc né ?save=true
|
/// Formati supportati: png (default), jpg/jpeg, pdf. Non supporta jpgEnc né ?save=true
|
||||||
/// (nessun alias-certificato applicabile a un sottostante puro).
|
/// (nessun alias-certificato applicabile a un sottostante puro).
|
||||||
|
/// ?years= (default 5, clamp 1-20): ampiezza storico prezzi caricato dalla SP.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpGet("underlying/{idUnderlyings:int}")]
|
[HttpGet("underlying/{idUnderlyings:int}")]
|
||||||
public async Task<IActionResult> GenerateChartUnderlying(
|
public async Task<IActionResult> GenerateChartUnderlying(
|
||||||
int idUnderlyings,
|
int idUnderlyings,
|
||||||
[FromQuery] int width = 1100,
|
[FromQuery] int width = 1100,
|
||||||
[FromQuery] int height = 700,
|
[FromQuery] int height = 700,
|
||||||
[FromQuery] string format = "png")
|
[FromQuery] string format = "png",
|
||||||
|
[FromQuery] int years = 5)
|
||||||
{
|
{
|
||||||
if (idUnderlyings <= 0)
|
if (idUnderlyings <= 0)
|
||||||
return BadRequest("IDUnderlyings non valido.");
|
return BadRequest("IDUnderlyings non valido.");
|
||||||
|
|
||||||
width = Math.Clamp(width, 400, 2000);
|
width = Math.Clamp(width, 400, 2000);
|
||||||
height = Math.Clamp(height, 300, 1500);
|
height = Math.Clamp(height, 300, 1500);
|
||||||
|
years = Math.Clamp(years, 1, 20);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var chartData = await _underlyingChartDataService.GetChartDataAsync(idUnderlyings);
|
var chartData = await _underlyingChartDataService.GetChartDataAsync(idUnderlyings, years);
|
||||||
|
|
||||||
if (chartData == null || chartData.Points.Count == 0)
|
if (chartData == null || chartData.Points.Count == 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace CertReports.Syncfusion.Services.Implementations;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IUnderlyingChartDataService
|
public interface IUnderlyingChartDataService
|
||||||
{
|
{
|
||||||
Task<UnderlyingChartData?> GetChartDataAsync(int idUnderlyings);
|
Task<UnderlyingChartData?> GetChartDataAsync(int idUnderlyings, int years = 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UnderlyingChartDataService : IUnderlyingChartDataService
|
public class UnderlyingChartDataService : IUnderlyingChartDataService
|
||||||
@@ -25,7 +25,7 @@ public class UnderlyingChartDataService : IUnderlyingChartDataService
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<UnderlyingChartData?> GetChartDataAsync(int idUnderlyings)
|
public async Task<UnderlyingChartData?> GetChartDataAsync(int idUnderlyings, int years = 5)
|
||||||
{
|
{
|
||||||
await using var conn = new SqlConnection(_connectionString);
|
await using var conn = new SqlConnection(_connectionString);
|
||||||
await conn.OpenAsync();
|
await conn.OpenAsync();
|
||||||
@@ -33,6 +33,7 @@ public class UnderlyingChartDataService : IUnderlyingChartDataService
|
|||||||
await using var cmd = new SqlCommand("cedlab_Chart_UnderlyingOnly", conn)
|
await using var cmd = new SqlCommand("cedlab_Chart_UnderlyingOnly", conn)
|
||||||
{ CommandType = CommandType.StoredProcedure };
|
{ CommandType = CommandType.StoredProcedure };
|
||||||
cmd.Parameters.AddWithValue("@IDUnderlyings", idUnderlyings);
|
cmd.Parameters.AddWithValue("@IDUnderlyings", idUnderlyings);
|
||||||
|
cmd.Parameters.AddWithValue("@Years", years);
|
||||||
|
|
||||||
var result = new UnderlyingChartData { IDUnderlyings = idUnderlyings };
|
var result = new UnderlyingChartData { IDUnderlyings = idUnderlyings };
|
||||||
|
|
||||||
@@ -42,10 +43,14 @@ public class UnderlyingChartDataService : IUnderlyingChartDataService
|
|||||||
if (result.Points.Count == 0)
|
if (result.Points.Count == 0)
|
||||||
result.Nome = ToStr(r, "Nome");
|
result.Nome = ToStr(r, "Nome");
|
||||||
|
|
||||||
|
int pxOrd = r.GetOrdinal("Px");
|
||||||
|
if (r.IsDBNull(pxOrd))
|
||||||
|
continue; // riga storica con solo Px_low/high/open valorizzati, no close
|
||||||
|
|
||||||
result.Points.Add(new UnderlyingChartPoint
|
result.Points.Add(new UnderlyingChartPoint
|
||||||
{
|
{
|
||||||
Date = r.GetDateTime(r.GetOrdinal("Px_date")),
|
Date = r.GetDateTime(r.GetOrdinal("Px_date")),
|
||||||
Px = Convert.ToDecimal(r.GetValue(r.GetOrdinal("Px"))),
|
Px = Convert.ToDecimal(r.GetValue(pxOrd)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ GET /api/chart/v2/{isin}[?format=png|jpg|jpeg|jpgEnc|pdf&width=&height=&save=tru
|
|||||||
### Grafico Sottostante standalone — parametri
|
### Grafico Sottostante standalone — parametri
|
||||||
|
|
||||||
```
|
```
|
||||||
GET /api/chart/underlying/{idUnderlyings}[?format=png|jpg|jpeg|pdf&width=&height=]
|
GET /api/chart/underlying/{idUnderlyings}[?format=png|jpg|jpeg|pdf&width=&height=&years=]
|
||||||
```
|
```
|
||||||
|
|
||||||
Mostra solo l'andamento storico prezzo di **un singolo sottostante** (`dbo.Underlyings`), identificato da `IDUnderlyings` — nessun ISIN certificato, nessuna barriera/strike/trigger, nessuna legenda. Titolo del grafico: `{Name} ({Ticker_bbg})`. SP: `cedlab_Chart_UnderlyingOnly`.
|
Mostra solo l'andamento storico prezzo di **un singolo sottostante** (`dbo.Underlyings`), identificato da `IDUnderlyings` — nessun ISIN certificato, nessuna barriera/strike/trigger, nessuna legenda. Titolo del grafico: `{Name} ({Ticker_bbg})`. SP: `cedlab_Chart_UnderlyingOnly`.
|
||||||
@@ -135,6 +135,9 @@ Mostra solo l'andamento storico prezzo di **un singolo sottostante** (`dbo.Under
|
|||||||
| Parametro | Valori | Effetto |
|
| Parametro | Valori | Effetto |
|
||||||
|-----------|--------|---------|
|
|-----------|--------|---------|
|
||||||
| `?format=` | `png` (default), `jpg`/`jpeg`, `pdf` | Formato output |
|
| `?format=` | `png` (default), `jpg`/`jpeg`, `pdf` | Formato output |
|
||||||
|
| `?years=` | intero, default `5`, clamp `1`-`20` | Ampiezza storico prezzi caricato dalla SP |
|
||||||
|
|
||||||
|
**Prezzo usato**: `Px_close` se `Underlyings.AdjustedPrices=0`, altrimenti `Px_closeadj`. Nessun fallback incrociato tra i due campi — se `Px_closeadj` non è backfillato prima di uno split/rettifica (es. Apple: valorizzato solo da gennaio 2019), lo storico restituito si ferma lì invece di mostrare un gradino artificiale mischiando dati adjusted/non-adjusted.
|
||||||
|
|
||||||
Non supporta `jpgEnc` né `?save=true` (nessun alias-certificato applicabile a un sottostante puro).
|
Non supporta `jpgEnc` né `?save=true` (nessun alias-certificato applicabile a un sottostante puro).
|
||||||
|
|
||||||
|
|||||||
@@ -8,14 +8,20 @@ GO
|
|||||||
-- Usata da UnderlyingChartDataService per il grafico standalone
|
-- Usata da UnderlyingChartDataService per il grafico standalone
|
||||||
-- sottostante.
|
-- sottostante.
|
||||||
--
|
--
|
||||||
-- Output (Px_date ASC, TOP 350 più recenti):
|
-- @Years: ampiezza storico in anni (default 5).
|
||||||
|
--
|
||||||
|
-- Output (Px_date ASC, ultimi @Years anni):
|
||||||
-- Nome VARCHAR -- "{Name} ({Ticker_bbg})", uguale su ogni riga
|
-- Nome VARCHAR -- "{Name} ({Ticker_bbg})", uguale su ogni riga
|
||||||
-- Px_date DATE
|
-- Px_date DATE
|
||||||
-- Px DECIMAL -- Px_close o Px_closeadj in base ad AdjustedPrices
|
-- Px DECIMAL -- Px_closeadj se AdjustedPrices=1, altrimenti
|
||||||
|
-- Px_close. Nessun fallback incrociato tra i due
|
||||||
|
-- campi: evita il gradino artificiale quando
|
||||||
|
-- Px_closeadj non è backfillato prima di uno split
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
|
|
||||||
CREATE OR ALTER PROCEDURE [dbo].[cedlab_Chart_UnderlyingOnly]
|
CREATE OR ALTER PROCEDURE [dbo].[cedlab_Chart_UnderlyingOnly]
|
||||||
@IDUnderlyings INT
|
@IDUnderlyings INT,
|
||||||
|
@Years INT = 5
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON;
|
SET NOCOUNT ON;
|
||||||
@@ -29,24 +35,17 @@ BEGIN
|
|||||||
|
|
||||||
IF @AdjustedPrices IS NULL RETURN; -- non trovato / cancellato / sospeso
|
IF @AdjustedPrices IS NULL RETURN; -- non trovato / cancellato / sospeso
|
||||||
|
|
||||||
;WITH Ranked AS
|
DECLARE @FromDate DATE = DATEADD(YEAR, -@Years, CAST(GETDATE() AS DATE));
|
||||||
(
|
|
||||||
SELECT
|
|
||||||
p.Px_date,
|
|
||||||
CASE WHEN @AdjustedPrices = 0 THEN p.Px_close ELSE p.Px_closeadj END AS Px,
|
|
||||||
ROW_NUMBER() OVER (ORDER BY p.Px_date DESC) AS rn
|
|
||||||
FROM dbo.Prices p
|
|
||||||
WHERE p.UnderlyingsID = @IDUnderlyings
|
|
||||||
AND (p.Px_low IS NOT NULL OR p.Px_high IS NOT NULL OR
|
|
||||||
p.Px_open IS NOT NULL OR p.Px_close IS NOT NULL)
|
|
||||||
)
|
|
||||||
SELECT
|
SELECT
|
||||||
CONCAT(u.Name, ' (', u.Ticker_bbg, ')') AS Nome,
|
CONCAT(u.Name, ' (', u.Ticker_bbg, ')') AS Nome,
|
||||||
r.Px_date,
|
p.Px_date,
|
||||||
r.Px
|
CASE WHEN @AdjustedPrices = 0 THEN p.Px_close ELSE p.Px_closeadj END AS Px
|
||||||
FROM Ranked r
|
FROM dbo.Prices p
|
||||||
CROSS JOIN (SELECT TOP 1 * FROM dbo.Underlyings WHERE IDUnderlyings = @IDUnderlyings) u
|
CROSS JOIN (SELECT TOP 1 * FROM dbo.Underlyings WHERE IDUnderlyings = @IDUnderlyings) u
|
||||||
WHERE r.rn <= 350
|
WHERE p.UnderlyingsID = @IDUnderlyings
|
||||||
ORDER BY r.Px_date ASC;
|
AND p.Px_date >= @FromDate
|
||||||
|
AND (CASE WHEN @AdjustedPrices = 0 THEN p.Px_close ELSE p.Px_closeadj END) IS NOT NULL
|
||||||
|
ORDER BY p.Px_date ASC;
|
||||||
END
|
END
|
||||||
GO
|
GO
|
||||||
|
|||||||
Reference in New Issue
Block a user