feat(core): update DTOs, Trade Republic client, Yahoo scrapers, and dynamic settings

This commit is contained in:
2026-08-14 23:55:02 +02:00
parent 3d8af3940b
commit 4f733bf8c3
511 changed files with 1990 additions and 1080548 deletions
-107
View File
@@ -1,107 +0,0 @@
using FinlyticCore.Dtos.Assets;
using FinlyticCore.Entities.Assets;
namespace FinlyticCore.Util;
public static class AssetMapper
{
public static AssetDto ToDto(this AssetEntity entity)
{
var dtoTags = entity.Tags.Select(t => new TagDto
{
Id = t.Id,
Name = t.Name,
Type = t.Type
}).ToList();
return entity switch
{
StockEntity stock => new StockDto
{
Isin = stock.Isin,
Name = stock.Name,
Type = stock.Type,
InstrumentCategory = stock.InstrumentCategory,
HasCfd = stock.HasCfd,
ImageId = stock.ImageId,
LastUpdatedAt = stock.LastUpdatedAt,
Tags = dtoTags,
DerivativeProductCategories = stock.DerivativeProductCategories
},
EtfEntity etf => new EtfDto
{
Isin = etf.Isin,
Name = etf.Name,
Type = etf.Type,
InstrumentCategory = etf.InstrumentCategory,
HasCfd = etf.HasCfd,
ImageId = etf.ImageId,
LastUpdatedAt = etf.LastUpdatedAt,
Tags = dtoTags,
DerivativeProductCategories = etf.DerivativeProductCategories,
EtfDescription = etf.EtfDescription,
MappedEtfIndexName = etf.MappedEtfIndexName,
Subtitle = etf.Subtitle,
SearchSubtitle = etf.SearchSubtitle
},
CryptoEntity crypto => new CryptoDto
{
Isin = crypto.Isin,
Name = crypto.Name,
Type = crypto.Type,
InstrumentCategory = crypto.InstrumentCategory,
HasCfd = crypto.HasCfd,
ImageId = crypto.ImageId,
LastUpdatedAt = crypto.LastUpdatedAt,
Tags = dtoTags,
Subtitle = crypto.Subtitle,
SearchSubtitle = crypto.SearchSubtitle
},
BondEntity bond => new BondDto
{
Isin = bond.Isin,
Name = bond.Name,
Type = bond.Type,
InstrumentCategory = bond.InstrumentCategory,
HasCfd = bond.HasCfd,
ImageId = bond.ImageId,
LastUpdatedAt = bond.LastUpdatedAt,
Tags = dtoTags,
BondIssuerName = bond.BondIssuerName,
SearchSubtitle = bond.SearchSubtitle
},
DerivativeEntity deriv => new DerivativeDto
{
Isin = deriv.Isin,
Name = deriv.Name,
Type = deriv.Type,
InstrumentCategory = deriv.InstrumentCategory,
HasCfd = deriv.HasCfd,
ImageId = deriv.ImageId,
LastUpdatedAt = deriv.LastUpdatedAt,
Tags = dtoTags,
DerivativeProductCategories = deriv.DerivativeProductCategories,
UnderlyingIsin = deriv.UnderlyingIsin
},
SyntheticEntity synth => new SyntheticDto
{
Isin = synth.Isin,
Name = synth.Name,
Type = synth.Type,
InstrumentCategory = synth.InstrumentCategory,
HasCfd = synth.HasCfd,
ImageId = synth.ImageId,
LastUpdatedAt = synth.LastUpdatedAt,
Tags = dtoTags,
DerivativeProductCategories = synth.DerivativeProductCategories
},
_ => throw new NotSupportedException($"Mapping for type {entity.GetType().Name} is not supported.")
};
}
public static List<AssetDto> ToDtoList(this IEnumerable<AssetEntity> entities)
{
return entities.Select(e => e.ToDto()).ToList();
}
}
@@ -41,6 +41,8 @@ namespace FinlyticCore.Util;
[JsonSerializable(typeof(FilteredAssetPayload))]
[JsonSerializable(typeof(AssetFundamentalsDto))]
[JsonSerializable(typeof(List<AssetFundamentalsDto>))]
[JsonSerializable(typeof(TickerInfoDto))]
[JsonSerializable(typeof(List<TickerInfoDto>))]
[JsonSerializable(typeof(CorporateEventDto))]
[JsonSerializable(typeof(List<CorporateEventDto>))]
[JsonSerializable(typeof(CalendarEventResponseDto))]
@@ -94,12 +96,18 @@ namespace FinlyticCore.Util;
[JsonSerializable(typeof(FinlyticCore.Models.Assets.GetValidAssetRequest))]
[JsonSerializable(typeof(FinlyticCore.Models.Assets.SearchAssetsRequest))]
[JsonSerializable(typeof(FinlyticCore.Models.Assets.GetDiscoveryAssetsRequest))]
[JsonSerializable(typeof(FinlyticCore.Models.TradeRepublic.TradeRepublicTickerResponse))]
[JsonSerializable(typeof(FinlyticCore.Models.TradeRepublic.TradeRepublicTickerRequest))]
[JsonSerializable(typeof(FinlyticCore.Models.TradeRepublic.TradeRepublicConnectRequest))]
[JsonSerializable(typeof(FinlyticCore.Models.TradeRepublic.TradeRepublicSearchRequest))]
[JsonSerializable(typeof(FinlyticCore.Models.TradeRepublic.TradeRepublicAssetResponse))]
[JsonSerializable(typeof(FinlyticCore.Models.Assets.GetDerivativesRequest))]
[JsonSerializable(typeof(FinlyticCore.Dtos.TradeRepublic.TradeRepublicTickerResponse))]
[JsonSerializable(typeof(FinlyticCore.Dtos.TradeRepublic.TradeRepublicTickerRequest))]
[JsonSerializable(typeof(FinlyticCore.Dtos.TradeRepublic.TradeRepublicConnectRequest))]
[JsonSerializable(typeof(FinlyticCore.Dtos.TradeRepublic.TradeRepublicSearchRequest))]
[JsonSerializable(typeof(FinlyticCore.Dtos.TradeRepublic.TradeRepublicAssetResponse))]
[JsonSerializable(typeof(FinlyticCore.Dtos.TradeRepublic.TradeRepublicDerivativesRequest))]
[JsonSerializable(typeof(FinlyticCore.Dtos.TradeRepublic.TradeRepublicDerivativesResponse))]
[JsonSerializable(typeof(FinlyticCore.Dtos.TradeRepublic.TradeRepublicStockDetailsRequest))]
[JsonSerializable(typeof(FinlyticCore.Dtos.TradeRepublic.TradeRepublicStockDetailsResponse))]
[JsonSerializable(typeof(YahooValueDto))]
[JsonSerializable(typeof(YahooQuoteTypeDto))]
[JsonSerializable(typeof(YahooQuoteSummaryResponseDto))]
[JsonSerializable(typeof(YahooQuoteSummaryResultDto))]
[JsonSerializable(typeof(YahooQuoteSummaryModulesDto))]
+28 -23
View File
@@ -242,37 +242,42 @@ public abstract class ManagedMqttClient : IDisposable
}
}
private async Task HandleIncomingMessageAsync(MqttApplicationMessageReceivedEventArgs e)
private Task HandleIncomingMessageAsync(MqttApplicationMessageReceivedEventArgs e)
{
try
_ = Task.Run(async () =>
{
var topic = e.ApplicationMessage.Topic;
var payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
_logger.LogInformation("MQTT message received on topic '{Topic}', length={Length}", topic, payload?.Length ?? 0);
// Intercept message if it belongs to the RPC response convention
if (topic.StartsWith("services/response/"))
try
{
var lastSlashIndex = topic.LastIndexOf('/');
if (lastSlashIndex != -1)
{
string correlationId = topic[(lastSlashIndex + 1)..];
var topic = e.ApplicationMessage.Topic;
var payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
_logger.LogInformation("MQTT message received on topic '{Topic}', length={Length}", topic, payload?.Length ?? 0);
if (_pendingRequests.TryRemove(correlationId, out var tcs))
// Intercept message if it belongs to the RPC response convention
if (topic.StartsWith("services/response/"))
{
var lastSlashIndex = topic.LastIndexOf('/');
if (lastSlashIndex != -1)
{
tcs.SetResult(payload);
return; // Sinks the message, avoiding triggering OnMessageReceivedAsync for active RPC handles
string correlationId = topic[(lastSlashIndex + 1)..];
if (_pendingRequests.TryRemove(correlationId, out var tcs))
{
tcs.SetResult(payload ?? string.Empty);
return; // Sinks the message, avoiding triggering OnMessageReceivedAsync for active RPC handles
}
}
}
}
// Regular Pub/Sub message propagation
await OnMessageReceivedAsync(topic, payload);
}
catch (Exception ex)
{
OnError(ex);
}
// Regular Pub/Sub message propagation
await OnMessageReceivedAsync(topic, payload ?? string.Empty);
}
catch (Exception ex)
{
OnError(ex);
}
});
return Task.CompletedTask;
}
private async Task HandleDisconnectAsync(MqttClientDisconnectedEventArgs e)