122 lines
7.9 KiB
C#
122 lines
7.9 KiB
C#
using System.Collections.Generic;
|
|
using System.Text.Json.Serialization;
|
|
using FinlyticCore.Dtos.TechnicalAnalysis;
|
|
|
|
namespace FinlyticCore.Dtos.Trading;
|
|
|
|
/// <summary>
|
|
/// Filter/pagination request for the admin-only evaluation-history RPC channel
|
|
/// (<c>MqttTopics.Channels.EngineGetEvaluationHistory</c>), served by FinlyticEngine and exposed to the Web UI
|
|
/// via <c>FinlyticBackend/Controllers/AdminEvaluationHistoryController</c>. All filters are optional and are
|
|
/// combined with logical AND; <see langword="null"/> means "do not filter on this field".
|
|
/// </summary>
|
|
/// <param name="FromUtc">Inclusive lower bound on <c>EngineEvaluationSnapshotEntity.EvaluatedAtUtc</c>.</param>
|
|
/// <param name="ToUtc">Inclusive upper bound on <c>EngineEvaluationSnapshotEntity.EvaluatedAtUtc</c>.</param>
|
|
/// <param name="OutcomeFilter">Restricts results to a single <see cref="OutcomeReason"/>.</param>
|
|
/// <param name="TriggerSourceFilter">Restricts results to a single <see cref="TriggerSource"/>.</param>
|
|
/// <param name="IsinOrSymbolSearch">
|
|
/// Case-sensitive substring search against both <c>Isin</c> and <c>Symbol</c> (matches either). Trimmed
|
|
/// server-side; blank/whitespace-only values are treated as "no search".
|
|
/// </param>
|
|
/// <param name="Page">1-based page number. Values below 1 are treated as 1 server-side.</param>
|
|
/// <param name="PageSize">
|
|
/// Requested page size. Server-side clamped to at least 1 and at most 200 (see
|
|
/// <c>EvaluationHistoryService.MaxPageSize</c>) so a caller cannot force FinlyticEngine to materialize/transmit
|
|
/// an unbounded result set in a single response.
|
|
/// </param>
|
|
public record GetEvaluationHistoryRequest(
|
|
[property: JsonPropertyName("fromUtc")] System.DateTime? FromUtc = null,
|
|
[property: JsonPropertyName("toUtc")] System.DateTime? ToUtc = null,
|
|
[property: JsonPropertyName("outcomeFilter")] OutcomeReason? OutcomeFilter = null,
|
|
[property: JsonPropertyName("triggerSourceFilter")] TriggerSource? TriggerSourceFilter = null,
|
|
[property: JsonPropertyName("isinOrSymbolSearch")] string? IsinOrSymbolSearch = null,
|
|
[property: JsonPropertyName("page")] int Page = 1,
|
|
[property: JsonPropertyName("pageSize")] int PageSize = 50
|
|
);
|
|
|
|
/// <summary>
|
|
/// One row of the evaluation history: the full, already-persisted record of a single
|
|
/// <c>TradeLifecycleService.EvaluateAssetAsync</c> run, mapped 1:1 from <c>EngineEvaluationSnapshotEntity</c>.
|
|
/// Every score field is the real, already-computed value - including the honest "0/default" values recorded
|
|
/// for the <see cref="OutcomeReason.NoTechnicalSetups"/> case, never a fabricated placeholder (Rules.md §4).
|
|
/// </summary>
|
|
public record EvaluationHistoryEntryDto(
|
|
[property: JsonPropertyName("id")] System.Guid Id,
|
|
[property: JsonPropertyName("isin")] string Isin,
|
|
[property: JsonPropertyName("symbol")] string Symbol,
|
|
[property: JsonPropertyName("technicalScore")] decimal TechnicalScore,
|
|
[property: JsonPropertyName("sentimentScore")] decimal SentimentScore,
|
|
[property: JsonPropertyName("fundamentalScore")] decimal FundamentalScore,
|
|
[property: JsonPropertyName("compositeOpportunityScore")] decimal CompositeOpportunityScore,
|
|
[property: JsonPropertyName("reliabilityBonus")] decimal ReliabilityBonus,
|
|
[property: JsonPropertyName("passedEarningsLockout")] bool PassedEarningsLockout,
|
|
[property: JsonPropertyName("daysToNextEarnings")] int? DaysToNextEarnings,
|
|
[property: JsonPropertyName("passedDividendGate")] bool PassedDividendGate,
|
|
[property: JsonPropertyName("daysToNextExDividend")] int? DaysToNextExDividend,
|
|
[property: JsonPropertyName("universeSource")] UniverseSource? UniverseSource,
|
|
[property: JsonPropertyName("universeEnteredAtUtc")] System.DateTime? UniverseEnteredAtUtc,
|
|
[property: JsonPropertyName("passedSimulationVeto")] bool PassedSimulationVeto,
|
|
[property: JsonPropertyName("passedAiValidation")] bool PassedAiValidation,
|
|
[property: JsonPropertyName("aiThesisSummary")] string AiThesisSummary,
|
|
[property: JsonPropertyName("outcomeReason")] OutcomeReason OutcomeReason,
|
|
[property: JsonPropertyName("triggerSource")] TriggerSource TriggerSource,
|
|
[property: JsonPropertyName("triggeredByUserId")] System.Guid? TriggeredByUserId,
|
|
[property: JsonPropertyName("proposalId")] System.Guid? ProposalId,
|
|
[property: JsonPropertyName("evaluatedAtUtc")] System.DateTime EvaluatedAtUtc
|
|
);
|
|
|
|
/// <summary>
|
|
/// Number of evaluation-history rows matching a given filter set that carry a specific <see cref="OutcomeReason"/>.
|
|
/// A typed list of these (rather than a <c>Dictionary<OutcomeReason,int></c>) is used on
|
|
/// <see cref="EvaluationHistorySummaryDto.CountsByOutcome"/> purely so this DTO round-trips through
|
|
/// System.Text.Json (including the AOT source-generated <c>FinlyticJsonSerializerContext</c>) without needing a
|
|
/// custom enum-keyed dictionary converter.
|
|
/// </summary>
|
|
public record OutcomeReasonCountDto(
|
|
[property: JsonPropertyName("outcomeReason")] OutcomeReason OutcomeReason,
|
|
[property: JsonPropertyName("count")] int Count
|
|
);
|
|
|
|
/// <summary>
|
|
/// Pre-aggregated headline numbers for the admin evaluation-history tab (e.g. "1.847 Analysen letzte 24h ·
|
|
/// 0 Vorschläge seit 14h · Ø-Score 66,7"), computed server-side so the Web UI never has to aggregate the full,
|
|
/// unpaginated result set itself. Every field except <see cref="LastProposalCreatedAtUtc"/> is scoped to
|
|
/// exactly the same filters as the paginated <see cref="EvaluationHistoryEntryDto"/> list it accompanies (see
|
|
/// <see cref="GetEvaluationHistoryResponse"/>) - only pagination (<c>Page</c>/<c>PageSize</c>) does not apply,
|
|
/// since these are totals over the whole filtered set, not just the current page.
|
|
/// </summary>
|
|
/// <param name="TotalEvaluations">Total number of snapshot rows matching the request's filters (unpaginated).</param>
|
|
/// <param name="CountsByOutcome">Breakdown of <see cref="TotalEvaluations"/> by <see cref="OutcomeReason"/>.</param>
|
|
/// <param name="AverageCompositeScore">
|
|
/// Average <c>CompositeOpportunityScore</c> across the filtered set; <c>0</c> when <see cref="TotalEvaluations"/> is 0.
|
|
/// </param>
|
|
/// <param name="ProposalsCreated">
|
|
/// Number of filtered rows whose <see cref="EvaluationHistoryEntryDto.OutcomeReason"/> is
|
|
/// <see cref="OutcomeReason.Approved"/> - i.e. the same value as the <see cref="OutcomeReason.Approved"/> entry
|
|
/// in <see cref="CountsByOutcome"/>, exposed directly so the UI does not need to search that list.
|
|
/// </param>
|
|
/// <param name="LastProposalCreatedAtUtc">
|
|
/// Timestamp of the most recently created <c>EngineTradeProposalEntity</c> across the ENTIRE proposals table -
|
|
/// deliberately NOT scoped to this request's <c>FromUtc</c>/<c>ToUtc</c> filters, because "how long since the
|
|
/// last real proposal" is a single wall-clock fact the admin wants regardless of which historical window they
|
|
/// are currently browsing. <see langword="null"/> only if no proposal has ever been created.
|
|
/// </param>
|
|
public record EvaluationHistorySummaryDto(
|
|
[property: JsonPropertyName("totalEvaluations")] int TotalEvaluations,
|
|
[property: JsonPropertyName("countsByOutcome")] List<OutcomeReasonCountDto> CountsByOutcome,
|
|
[property: JsonPropertyName("averageCompositeScore")] decimal AverageCompositeScore,
|
|
[property: JsonPropertyName("proposalsCreated")] int ProposalsCreated,
|
|
[property: JsonPropertyName("lastProposalCreatedAtUtc")] System.DateTime? LastProposalCreatedAtUtc
|
|
);
|
|
|
|
/// <summary>
|
|
/// Full response for the evaluation-history RPC channel: a page of matching rows, the total match count (for
|
|
/// pagination), and a pre-aggregated <see cref="Summary"/> so the Web UI never needs a second round trip (and a
|
|
/// second, potentially-inconsistent set of filters) just to render a header line above the table.
|
|
/// </summary>
|
|
public record GetEvaluationHistoryResponse(
|
|
[property: JsonPropertyName("totalCount")] int TotalCount,
|
|
[property: JsonPropertyName("entries")] List<EvaluationHistoryEntryDto> Entries,
|
|
[property: JsonPropertyName("summary")] EvaluationHistorySummaryDto Summary
|
|
);
|