feat(engine): add FinlyticEngine microservice with trade lifecycle, AI reasoning gate, composite scoring, and unit tests
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FinlyticEngine.Services.Ai;
|
||||
|
||||
/// <summary>
|
||||
/// Outgoing request body for the n8n AI trade-validation webhook. Service-local (not FinlyticCore): this
|
||||
/// shape is an integration detail of <see cref="AiReasoningGateService"/> only, not a cross-service MQTT
|
||||
/// contract (Rules.md §3 - core placement applies to data shared across services, not to a single service's
|
||||
/// own outbound HTTP integration). Serialized with <c>JsonNamingPolicy.CamelCase</c>, so every property here
|
||||
/// reaches n8n as camelCase without needing per-property <c>[JsonPropertyName]</c> attributes.
|
||||
/// </summary>
|
||||
public record N8nValidationRequestPayload(
|
||||
string Instructions,
|
||||
N8nAssetSection Asset,
|
||||
N8nTechnicalSetupSection TechnicalSetup,
|
||||
N8nWatchlistContextSection? WatchlistContext,
|
||||
N8nSentimentSection Sentiment,
|
||||
N8nFundamentalsSection Fundamentals,
|
||||
N8nReliabilitySection? Reliability,
|
||||
N8nScoreBreakdownSection ScoreBreakdown
|
||||
);
|
||||
|
||||
public record N8nAssetSection(string Isin, string Symbol, decimal CurrentPrice);
|
||||
|
||||
public record N8nPatternSection(string Type, string Bias, decimal QualityScore, string Description);
|
||||
|
||||
public record N8nTechnicalSetupSection(
|
||||
string Strategy,
|
||||
string StrategyName,
|
||||
string Direction,
|
||||
decimal Entry,
|
||||
decimal StopLoss,
|
||||
decimal TakeProfit1,
|
||||
decimal RiskRewardRatio,
|
||||
decimal QualityScore,
|
||||
string Rationale,
|
||||
string? MarketRegime,
|
||||
List<N8nPatternSection> TriggeringPatterns,
|
||||
Dictionary<string, decimal> IndicatorSnapshot
|
||||
);
|
||||
|
||||
/// <summary>Why FinlyticTechnicals was even scanning this ISIN (see <c>TechnicalUniverseManager</c>).</summary>
|
||||
public record N8nWatchlistContextSection(string Source, DateTime EnteredAtUtc);
|
||||
|
||||
public record N8nSentimentSection(string Label, double WeightedScore, string Trend, string LatestHighlight);
|
||||
|
||||
/// <summary>
|
||||
/// Fundamental data + the two temporal suppression gates (<see cref="Scoring.ScoringResult.PassedEarningsLockout"/>/
|
||||
/// <see cref="Scoring.ScoringResult.PassedDividendGate"/>). Ratio fields (<c>ReturnOnEquityRatio</c> etc.) are
|
||||
/// forwarded as raw fractions (e.g. <c>0.15</c> = 15%) exactly as stored, rather than guessing a ×100
|
||||
/// conversion that could silently misrepresent the source data (Rules.md §4). <see cref="OperatingMarginPercent"/>
|
||||
/// is the one exception: an honest, explicitly computed ratio (OperatingIncome / TotalRevenue × 100), only
|
||||
/// populated when both inputs are real numbers.
|
||||
/// </summary>
|
||||
public record N8nFundamentalsSection(
|
||||
decimal? ForwardPe,
|
||||
decimal? OperatingMarginPercent,
|
||||
decimal? RevenueGrowthYoYRatio,
|
||||
decimal? ReturnOnEquityRatio,
|
||||
decimal? DebtToEquity,
|
||||
decimal? FreeCashFlow,
|
||||
string? ConsensusRating,
|
||||
decimal? PriceTargetMean,
|
||||
decimal? ShortPercentOfFloatRatio,
|
||||
int? DaysToEarnings,
|
||||
bool PassedEarningsLockout,
|
||||
int? DaysToNextExDividend,
|
||||
bool PassedDividendGate
|
||||
);
|
||||
|
||||
/// <summary>FinlyticSimulation's backtest verdict for this exact (Isin, StrategyKey) - see <see cref="IAiReasoningGateService"/>.</summary>
|
||||
public record N8nReliabilitySection(
|
||||
decimal ReliabilityScore,
|
||||
decimal WinRatePercent,
|
||||
decimal ProfitFactor,
|
||||
int SampleTradeCount,
|
||||
bool IsStrategyApprovedForAsset,
|
||||
string RecommendedAction
|
||||
);
|
||||
|
||||
public record N8nScoreBreakdownSection(
|
||||
decimal CompositeScore,
|
||||
decimal TechnicalScore,
|
||||
decimal SentimentScore,
|
||||
decimal FundamentalScore,
|
||||
decimal ReliabilityBonus
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Expected shape of a successful n8n webhook response - deliberately identical field-for-field to
|
||||
/// <see cref="FinlyticCore.Dtos.Trading.AiValidationResultDto"/> (camelCase JSON) instead of the previous,
|
||||
/// undocumented ad hoc vocabulary (<c>status</c>/<c>action_recommendation</c>/<c>raw_validation_result.*</c>)
|
||||
/// that no prompt ever actually specified. <see cref="Confidence"/> is nullable because a validator that
|
||||
/// declines to give a numeric confidence must not have one fabricated for it (Rules.md §4).
|
||||
/// </summary>
|
||||
public record N8nValidationResponsePayload(
|
||||
bool? IsApproved,
|
||||
decimal? Confidence,
|
||||
string? ThesisSummary,
|
||||
string? InvalidationReason,
|
||||
List<string>? KeyCatalysts,
|
||||
List<string>? IdentifiedRisks
|
||||
);
|
||||
Reference in New Issue
Block a user