feat(backend): add admin evaluation history, engine, simulation, bot API controllers and global exception middleware

This commit is contained in:
2026-08-24 21:37:32 +02:00
parent 6974b2075b
commit 676496b77d
37 changed files with 88203 additions and 83075 deletions
@@ -7,7 +7,6 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using FinlyticAssets.Models;
using FinlyticAssets.Util;
using FinlyticBackend.Database;
using FinlyticBackend.Util;
@@ -268,6 +267,42 @@ public class AssetsController : ControllerBase
return NotFound(new { message = $"Keine technische Analyse für Asset '{normalizedSymbol}' verfügbar." });
}
/// <summary>
/// Liest den aktuellen Live-Kurs eines Assets oder Derivats via TR MQTT RPC.
/// </summary>
[HttpGet("{isin}/live")]
public async Task<IActionResult> GetLivePrice([FromRoute] string isin)
{
string normalizedSymbol = isin.Trim().ToUpperInvariant();
if (string.IsNullOrWhiteSpace(normalizedSymbol))
{
return BadRequest(new { message = "Eine gültige ISIN ist erforderlich." });
}
try
{
if (_mqttClient.IsConnected)
{
var rpcResult = await _mqttClient.SendRpcRequestAsync<LivePriceDto, IsinRequest>(
"tr_GetLivePrice",
new IsinRequest(normalizedSymbol),
TimeSpan.FromSeconds(5)
);
if (rpcResult != null && rpcResult.CurrentPrice > 0m)
{
return Ok(rpcResult);
}
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "RPC tr_GetLivePrice für Symbol '{Symbol}' fehlgeschlagen.", normalizedSymbol);
}
return NotFound(new { message = $"Kein Live-Kurs für Asset '{normalizedSymbol}' verfügbar." });
}
/// <summary>
/// Liest verfügbare Derivate (Knock-Outs, Optionsscheine etc.) für ein Basiswert-Asset via FinlyticAssets MQTT RPC.
/// </summary>
@@ -304,7 +339,7 @@ public class AssetsController : ControllerBase
ForceRefresh: forceRefresh
);
var rpcResult = await _mqttClient.SendRpcRequestAsync<List<AssetDto>, GetDerivativesRequest>(
var rpcResult = await _mqttClient.SendRpcRequestAsync<List<DerivativeDto>, GetDerivativesRequest>(
"assets_GetDerivatives",
req,
TimeSpan.FromSeconds(30)
@@ -312,7 +347,7 @@ public class AssetsController : ControllerBase
if (rpcResult != null)
{
var derivatives = rpcResult.OfType<DerivativeDto>().ToList();
var derivatives = rpcResult;
if (minLeverage.HasValue)
{
@@ -365,6 +400,10 @@ public class AssetsController : ControllerBase
/// <summary>
/// Serviert das SVG-Logo direkt aus dem gemounteten Docker Volume (Volumes.LogosRelativePath).
/// Explicit, sanctioned exception to "every route requires authentication" (Rules.md §7), not an
/// oversight: logos are static, non-user-specific assets (looked up only by ISIN), and generic image
/// loaders/`&lt;img&gt;` tags do not attach an <c>Authorization</c> header by default. Keeping this endpoint
/// anonymous lets every image loader render it without special-casing headers.
/// </summary>
[HttpGet("/api/v1/logo/{isin}")]
[AllowAnonymous]