using System.Text.Json.Serialization; namespace FinlyticCore.Dtos.Trading; [JsonConverter(typeof(JsonStringEnumConverter))] public enum ExecutionMode { SignalProposal, // Reines Signal zur manuellen Ansicht ManualTradeRepublic, // Händisch bei Trade Republic ausgeführt PaperTradingBot // Vollautomatisch im Paper-Trading-Modus } [JsonConverter(typeof(JsonStringEnumConverter))] public enum TradeStatus { Proposed, // KI-geprüfter Vorschlag, wartet auf Ausführung Active, // Mindestens 1 Fill ausgeführt, Trade läuft BreakEvenTriggered, // Kurs hat TP1 erreicht, SL liegt auf Mischkurs Tp1Hit, // Teilverkauf 1 ausgeführt Tp2Hit, // Teilverkauf 2 ausgeführt Closed, // Vollständig mit Gewinn glattgestellt StoppedOut, // Durch Stop-Loss beendet Invalidated, // Kurs hat Invalidation erreicht, bevor Einstieg erfolgte Expired // Gültigkeitsfenster abgelaufen } [JsonConverter(typeof(JsonStringEnumConverter))] public enum InstrumentCategoryType { Stock, Etf, TurboLong, TurboShort, FactorCertificate } /// /// Identifies whether an EngineEvaluationSnapshotEntity row was produced by the autonomous /// OpportunityPollerBackgroundService scan loop or by an on-demand, human-initiated call (Web UI /// "Analyze now" / EngineController.EvaluateAsset / AnalyzeController.TriggerManualAnalysis). /// is deliberately value 0 (the default) so that snapshot rows written before /// this field existed - and any future row where the caller genuinely failed to specify a source - are never /// silently mis-reported as one of the two real sources (Rules.md §4: no fabricated data, an honest /// "we don't know" beats a fabricated default of ). /// [JsonConverter(typeof(JsonStringEnumConverter))] public enum TriggerSource { Unknown = 0, Automatic = 1, Manual = 2 } /// /// Classifies why a single asset evaluation in TradeLifecycleService.EvaluateAssetAsync did or did not /// result in a trade proposal. is deliberately value 0 (the default) so snapshot /// rows persisted before this field existed read honestly as "reason unknown" rather than fabricating a /// specific-looking cause (Rules.md §4). See the "DetermineOutcomeReason" doc comment in /// TradeLifecycleService for the exact priority order applied when more than one gate failed at once. /// [JsonConverter(typeof(JsonStringEnumConverter))] public enum OutcomeReason { Unknown = 0, /// The AI reasoning gate approved the opportunity and a EngineTradeProposalEntity was created. Approved = 1, /// /// ScoringResult.CompositeScore stayed below Engine.MinCompositeScore and the evaluation was /// not forced, so the AI reasoning gate was never even consulted (a synthetic rule-based rejection was /// recorded instead). /// BelowScoreThreshold = 2, /// The asset is within the earnings blackout window (Engine.EarningsLockoutDays). EarningsLockout = 3, /// FinlyticSimulation's backtest-reliability matrix vetoed this strategy/asset combination. SimulationVeto = 4, /// /// The composite score cleared the minimum threshold (or the evaluation was forced) and neither the /// earnings-lockout nor the simulation-veto gate blocked it, but the AI reasoning gate itself - whether a /// real AI call or one of its own rule-based fallbacks (gate disabled, webhook unreachable) - still declined. /// AiRejected = 5, /// /// No technical setup could be produced for the ISIN at all (FinlyticTechnicals returned nothing), or the /// ISIN itself was blank - in both cases the pipeline never reached scoring, so every score field on the /// snapshot is 0/default rather than fabricated. /// NoTechnicalSetups = 6, /// /// Not a real rejection: the evaluation genuinely cleared every gate and the AI reasoning gate approved the /// opportunity (PassedAiValidation on this same row is ), but an active, /// non-expired EngineTradeProposalEntity for the same UnderlyingIsin already exists, so no /// second, near-identical proposal row was created and no finlytic/engine/proposals/created MQTT /// event was re-broadcast. Exists specifically to stop the autonomous scanner from spamming a fresh /// proposal (and a fresh push event to every connected client) every single poll cycle for as long as one /// asset stays above the approval threshold - the underlying bug this value was introduced to fix. /// DuplicateActiveProposal = 7, /// The asset is within the ex-dividend blackout window (Engine.DividendGateDays). DividendGate = 8 }