Skip to content

Commit

Permalink
Implementação de Download do Histórico no formato Excel
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfariakof committed Jun 11, 2024
1 parent 3c48302 commit 8e97856
Show file tree
Hide file tree
Showing 16 changed files with 415 additions and 106 deletions.
3 changes: 3 additions & 0 deletions Business/HomeBroker.Business.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
<Nullable>enable</Nullable>
<EnableHotReload>true</EnableHotReload>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="EPPlus" Version="7.1.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Repository\HomeBroker.Repository.csproj" />
</ItemGroup>
Expand Down
77 changes: 69 additions & 8 deletions Business/HomeBrokerBusiness.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
using Repository.Interfaces;
using HomeBroker.Domain.Charts.Agreggates.Factory;
using HomeBroker.Business.Cache;
using System.Drawing;
using OfficeOpenXml;
using OfficeOpenXml.Style;

namespace Business;
public class HomeBrokerBusiness: IHomeBrokerBusiness
{
public class HomeBrokerBusiness : IHomeBrokerBusiness
{
private readonly IHomeBrokerRepository _homeBrokerRepository;
private readonly IMagazineLuizaHistoryPriceFactory _historyPriceFactory;
private readonly IMagazineLuizaHistoryPriceFactory _historyPriceFactory;
private readonly TimeSpan CACHE_EXPIRATION_TIME = TimeSpan.FromMinutes(20);
private readonly TimeSpan CACHE_DELAY = TimeSpan.FromMinutes(30);
private Dictionary<Period, CacheEntry<List<MagazineLuizaHistoryPrice>>> _historyCache;
Expand All @@ -26,7 +29,7 @@ public HomeBrokerBusiness(IMagazineLuizaHistoryPriceFactory magazineLuizaHistory
_cacheCleanupTimer = new Timer(DisposeCache, null, TimeSpan.Zero, CACHE_DELAY);
}

public async Task<List<MagazineLuizaHistoryPrice>> GetHistoryData(Period period)
public async Task<List<MagazineLuizaHistoryPrice>> GetHistoryData(Period period)
{
if (_historyCache.TryGetValue(period, out var cacheEntry) && !cacheEntry.IsExpired())
{
Expand Down Expand Up @@ -54,13 +57,13 @@ public async Task<Sma> GetSMA(Period period)
var sma = new Sma(historyData.Select(price => price.Close).ToList());
return sma;
}
catch
catch
{
throw new ArgumentException("Erro ao gerar SMA.");
}
}

public async Task<Ema> GetEMA(int periodDays, Period period)
public async Task<Ema> GetEMA(int periodDays, Period period)
{
try
{
Expand All @@ -79,15 +82,72 @@ public async Task<MACD> GetMACD(Period period)
try
{
var historyData = await GetHistoryData(period);
var macd = new MACD(historyData.Select(price => price.Close).ToList());
var macd = new MACD(historyData.Select(price => price.Close).ToList());
return macd;
}
catch
catch
{
throw new ArgumentException("Erro ao gerar MACD.");
}
}

public async Task<MemoryStream> GenerateExcelHistory(Period period)
{
var historyData = await GetHistoryData(period);

using (var package = new ExcelPackage())
{
var worksheet = package.Workbook.Worksheets.Add("Histórico");

// Adiciona os cabeçalhos
worksheet.Cells["A1"].Value = "Data";
worksheet.Cells["B1"].Value = "Preço de Abertura";
worksheet.Cells["C1"].Value = "Preço Mais Alto";
worksheet.Cells["D1"].Value = "Preço Mais Baixo";
worksheet.Cells["E1"].Value = "Preço de Fechamento";
worksheet.Cells["F1"].Value = "Preço de Fechamento Ajustado";
worksheet.Cells["G1"].Value = "Volume";

// Formata os cabeçalhos
using (var range = worksheet.Cells["A1:G1"])
{
range.Style.Font.Bold = true;
range.Style.Fill.PatternType = ExcelFillStyle.Solid;
range.Style.Fill.BackgroundColor.SetColor(Color.LightGray);
range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
}

// Adiciona os dados e formata as células
for (int i = 0; i < historyData.Count; i++)
{
var item = historyData[i];
worksheet.Cells[i + 2, 1].Value = item.Date;
worksheet.Cells[i + 2, 1].Style.Numberformat.Format = "dd/MM/yyyy";
worksheet.Cells[i + 2, 2].Value = item.Open;
worksheet.Cells[i + 2, 2].Style.Numberformat.Format = "R$ #,######0.0000000";
worksheet.Cells[i + 2, 3].Value = item.High;
worksheet.Cells[i + 2, 3].Style.Numberformat.Format = "R$ #,######0.0000000";
worksheet.Cells[i + 2, 4].Value = item.Low;
worksheet.Cells[i + 2, 4].Style.Numberformat.Format = "R$ #,######0.0000000";
worksheet.Cells[i + 2, 5].Value = item.Close;
worksheet.Cells[i + 2, 5].Style.Numberformat.Format = "R$ #,######0.0000000";
worksheet.Cells[i + 2, 6].Value = item.AdjClose;
worksheet.Cells[i + 2, 6].Style.Numberformat.Format = "R$ #,######0.0000000";
worksheet.Cells[i + 2, 7].Value = item.Volume;
worksheet.Cells[i + 2, 7].Style.Numberformat.Format = "#,0";
}

// Ajusta a largura das colunas
worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns();

var stream = new MemoryStream();
package.SaveAs(stream);
stream.Position = 0;

return stream;
}
}

private void DisposeCache(object state)
{
lock (_lock)
Expand All @@ -99,4 +159,5 @@ private void DisposeCache(object state)
}
}
}

}
2 changes: 2 additions & 0 deletions Business/Interfaces/IHomeBrokerBusiness.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ public interface IHomeBrokerBusiness
public Task<Sma> GetSMA(Period period);
public Task<Ema> GetEMA(int periodDays, Period period);
public Task<MACD> GetMACD(Period period);
public Task<MemoryStream> GenerateExcelHistory(Period period);

}
33 changes: 30 additions & 3 deletions HomeBrokerSPA/Controllers/ChartHomeBrokerController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public async Task<IActionResult> Get([FromRoute] DateTime StartDate, [FromRoute]
var result = await _homeBrokerBusiness.GetHistoryData(period);
return Ok(result);
}
catch
catch
{
return NoContent();
}
Expand Down Expand Up @@ -106,7 +106,7 @@ public async Task<IActionResult> GetEMA([FromRoute] int PeriodDays, [FromRoute]
public async Task<IActionResult> GetMACD([FromRoute] DateTime StartDate, [FromRoute] DateTime EndDate)
{
try
{
{
var period = new Period(StartDate, EndDate);
var result = await _homeBrokerBusiness.GetMACD(period);
return Ok(result);
Expand All @@ -116,4 +116,31 @@ public async Task<IActionResult> GetMACD([FromRoute] DateTime StartDate, [FromRo
return BadRequest(new { message = ex.Message });
}
}
}

/// <summary>
/// Gera e baixa um arquivo Excel com dados históricos de preço para o período especificado.
/// </summary>
/// <param name="StartDate">A data de início do período.</param>
/// <param name="EndDate">A data de término do período.</param>
/// <returns>O arquivo Excel com os dados históricos de preço.</returns>
[HttpGet("DownloadHistory/{StartDate}/{EndDate}")]
[ProducesResponseType(200, Type = typeof(FileResult))]
[ProducesResponseType(400, Type = typeof(object))]
public async Task<IActionResult> DownloadHistory([FromRoute] DateTime StartDate, [FromRoute] DateTime EndDate)
{
try
{
var period = new Period(StartDate, EndDate);
var stream = await _homeBrokerBusiness.GenerateExcelHistory(period);

var contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
var fileName = $"History_{StartDate:yyyyMMdd}_{EndDate:yyyyMMdd}.xlsx";

return File(stream, contentType, fileName);
}
catch (Exception ex)
{
return BadRequest(new { message = ex.Message });
}
}
}
4 changes: 2 additions & 2 deletions HomeBrokerSPA/HomeBroker.SPA.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="EPPlus" Version="7.1.3" />
<PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="8.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<ProjectReference Include="..\Business\HomeBroker.Business.csproj" />
Expand Down Expand Up @@ -44,8 +45,7 @@
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
</Target>


<Target Name="DockerDebuggeeProgram" BeforeTargets="Build" Condition=" '$(Configuration)' != 'Debug'">
<Target Name="DockerDebuggeeProgram" BeforeTargets="Build" Condition="'$(Configuration)' != 'Debug' And '$(Configuration)' != 'Staging'">
<!-- Ensure angular project compilation -->
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build -- --configuration production" />
</Target>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@

<h2 class=" mx-2 m-0 fs-4">
<a href="https://br.financas.yahoo.com/quote/MGLU3.SA/history?interval=1d&filter=history&frequency=1d" target="_blank" rel="" >Magazine Luiza S.A. (MGLU3.SA)</a>
</h2>
<div class="input-group mt-0 d-flex flex-row ">
<h2 class=" mx-2 m-0 fs-4">
<a href="https://br.financas.yahoo.com/quote/MGLU3.SA/history?interval=1d&filter=history&frequency=1d" target="_blank" rel="">Magazine Luiza S.A. (MGLU3.SA)</a>
</h2>
<button (click)="downloadHistory()" class="btn btn-primary ">Download Excel</button>
</div>
<div class="table-content">
<table class="table table-bordered table-sm table-responsive table-striped w-100" *ngIf="magazineLuizaHistoryPrices">
<thead >
<table class="table table-bordered table-sm table-responsive table-striped w-100" *ngIf="magazineLuizaHistoryPrices">
<thead>
<tr class="table-dark">
<th scope="col">Data</th>
<th scope="col">Abertura</th>
Expand All @@ -15,39 +17,59 @@ <h2 class=" mx-2 m-0 fs-4">
<th scope="col">Volume</th>
</tr>
</thead>
<tbody >
<tbody>
<tr class="d-table-row" *ngFor="let magazineLuizaHistoryPrice of magazineLuizaHistoryPrices">
<td>{{ formatCustomDate(magazineLuizaHistoryPrice.date) }}</td>
<td>{{ magazineLuizaHistoryPrice.open.toLocaleString('pt-BR', {
<td>
{{
magazineLuizaHistoryPrice.open.toLocaleString('pt-BR', {
style: 'currency',
currency: 'BRL',
minimumFractionDigits: 2,
maximumFractionDigits: 2
}) }}</td>
<td>{{ magazineLuizaHistoryPrice.high.toLocaleString('pt-BR', {
maximumFractionDigits: 3
})
}}
</td>
<td>
{{
magazineLuizaHistoryPrice.high.toLocaleString('pt-BR', {
style: 'currency',
currency: 'BRL',
minimumFractionDigits: 2,
maximumFractionDigits: 2
}) }}</td>
<td>{{ magazineLuizaHistoryPrice.low.toLocaleString('pt-BR', {
maximumFractionDigits: 3
})
}}
</td>
<td>
{{
magazineLuizaHistoryPrice.low.toLocaleString('pt-BR', {
style: 'currency',
currency: 'BRL',
minimumFractionDigits: 2,
maximumFractionDigits: 2
}) }}</td>
<td>{{ magazineLuizaHistoryPrice.close.toLocaleString('pt-BR', {
maximumFractionDigits: 3
})
}}
</td>
<td>
{{
magazineLuizaHistoryPrice.close.toLocaleString('pt-BR', {
style: 'currency',
currency: 'BRL',
minimumFractionDigits: 2,
maximumFractionDigits: 2
}) }}</td>
<td>{{ magazineLuizaHistoryPrice.adjClose.toLocaleString('pt-BR', {
maximumFractionDigits: 3
})
}}
</td>
<td>
{{
magazineLuizaHistoryPrice.adjClose.toLocaleString('pt-BR', {
style: 'currency',
currency: 'BRL',
minimumFractionDigits: 2,
maximumFractionDigits: 2
}) }}</td>
maximumFractionDigits: 3
})
}}
</td>
<td>{{ magazineLuizaHistoryPrice.volume.toLocaleString('pt-BR') }}</td>
</tr>
</tbody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ export class AcoesComponent {
this.magazineLuizaHistoryPrices = await this.chartService.get(this.obsStartDate.startDate, this.obsEndDate.endDate);
}

downloadHistory = async () => {
try {
const blob = await this.chartService.downloadHistory(this.obsStartDate.startDate.toString(), this.obsEndDate.endDate.toString());
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `historico_${this.obsStartDate.startDate.toString()}_${this.obsEndDate.endDate.toString()}.xlsx`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
}
catch (error) {
console.error('Erro ao fazer o download do arquivo Excel:', error);
}
}

formatCustomDate(date: any): string {
return dayjs(date).locale('pt-Br').format('DD/MM/YYYY');
}
Expand Down
Loading

0 comments on commit 8e97856

Please sign in to comment.