feat(assets): update scanner background service, derivative entities, and dynamic settings
This commit is contained in:
@@ -4,8 +4,8 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FinlyticAssets.Models;
|
||||
using FinlyticCore.Dtos.TradeRepublic;
|
||||
using FinlyticCore.Models.Assets;
|
||||
using FinlyticCore.Models.TradeRepublic;
|
||||
using FinlyticCore.Services.TradeRepublic;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using FinlyticAssets.Database;
|
||||
using FinlyticAssets.Entities;
|
||||
using FinlyticCore.Entities.Assets;
|
||||
using FinlyticCore.Models.TradeRepublic;
|
||||
using FinlyticCore.Dtos.TradeRepublic;
|
||||
using FinlyticCore.Services.TradeRepublic;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@@ -21,6 +20,7 @@ public interface IAssetsDbService
|
||||
public Task UpdateAssetImageIdAsync(string isin, string imageId);
|
||||
public Task<bool> DeleteAssetAsync(string isin);
|
||||
public Task<List<AssetEntity>> GetDiscoveryAssetsAsync(int limit = 15);
|
||||
public Task<List<DerivativeEntity>> GetDerivativesByUnderlyingAsync(string underlyingIsin, string optionType = "long", bool forceRefresh = false, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -393,6 +393,82 @@ public class AssetsDbService : IAssetsDbService
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Inherits documentation from interface.</summary>
|
||||
public async Task<List<DerivativeEntity>> GetDerivativesByUnderlyingAsync(string underlyingIsin, string optionType = "long", bool forceRefresh = false, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var targetOptionType = optionType.Equals("short", StringComparison.OrdinalIgnoreCase) ? OptionType.Short : OptionType.Long;
|
||||
var cutoff = DateTime.UtcNow.AddDays(-7);
|
||||
|
||||
if (!forceRefresh)
|
||||
{
|
||||
var cached = await _context.TradeRepublicAssets
|
||||
.OfType<DerivativeEntity>()
|
||||
.AsNoTracking()
|
||||
.Include(a => a.Tags)
|
||||
.Where(d => d.UnderlyingIsin == underlyingIsin && d.OptionType == targetOptionType && d.LastUpdatedAt >= cutoff)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (cached.Count > 0)
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
|
||||
var trReq = new TradeRepublicDerivativesRequest(Underlying: underlyingIsin, OptionType: optionType, ProductCategory: "knockOutProduct", PageSize: 50, After: "0");
|
||||
var trResponse = await _tradeRepublicService.GetDerivativesAsync(trReq, cancellationToken);
|
||||
if (trResponse?.Results != null && trResponse.Results.Count > 0)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var isins = trResponse.Results.Select(r => r.Isin).Distinct().ToList();
|
||||
var existingDerivatives = await _context.TradeRepublicAssets
|
||||
.OfType<DerivativeEntity>()
|
||||
.Where(d => isins.Contains(d.Isin))
|
||||
.ToDictionaryAsync(d => d.Isin, cancellationToken);
|
||||
|
||||
foreach (var item in trResponse.Results)
|
||||
{
|
||||
if (!existingDerivatives.TryGetValue(item.Isin, out var entity))
|
||||
{
|
||||
entity = new DerivativeEntity
|
||||
{
|
||||
Isin = item.Isin,
|
||||
InstrumentCategory = "derivative",
|
||||
Type = "derivative"
|
||||
};
|
||||
await _context.TradeRepublicAssets.AddAsync(entity, cancellationToken);
|
||||
}
|
||||
|
||||
entity.UnderlyingIsin = underlyingIsin;
|
||||
entity.OptionType = item.OptionType.Equals("short", StringComparison.OrdinalIgnoreCase) ? OptionType.Short : OptionType.Long;
|
||||
entity.ProductCategoryName = item.ProductCategoryName;
|
||||
entity.NextGenProductCategoryName = item.NextGenProductCategoryName;
|
||||
entity.Strike = item.Strike ?? 0m;
|
||||
entity.Barrier = item.Barrier ?? 0m;
|
||||
entity.Leverage = item.Leverage ?? 0m;
|
||||
entity.Size = item.Size;
|
||||
entity.Factor = item.Factor;
|
||||
entity.Delta = item.Delta;
|
||||
entity.Currency = item.Currency ?? "EUR";
|
||||
entity.Expiry = DateTime.TryParse(item.Expiry, out var exp) ? exp : null;
|
||||
entity.Issuer = item.Issuer;
|
||||
entity.IssuerDisplayName = item.IssuerDisplayName;
|
||||
entity.IssuerImageId = item.IssuerImageId;
|
||||
entity.ImageId = item.ImageId;
|
||||
entity.Name = $"{item.IssuerDisplayName} {item.NextGenProductCategoryName} ({item.OptionType.ToUpper()})";
|
||||
entity.LastUpdatedAt = now;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
return await _context.TradeRepublicAssets
|
||||
.OfType<DerivativeEntity>()
|
||||
.AsNoTracking()
|
||||
.Include(a => a.Tags)
|
||||
.Where(d => d.UnderlyingIsin == underlyingIsin && d.OptionType == targetOptionType)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
#region Helper & Mapping Methods
|
||||
|
||||
private AssetEntity MapDtoToEntity(TradeRepublicAsset dto)
|
||||
|
||||
Reference in New Issue
Block a user